Ruby DataMapper::ImmutableError - ruby

get '/watch/:id' do |id|
#results = Twitchtvst.all( :fields => [:Twitchtv ],
:conditions => { :user_id => "#{id}" }
)
#p #results.inspect
#results.each do |result|
puts result.id
end
erb :mystream
end
I get this error message immutable resource cannot be lazy loaded. How do I fix this?
The Error message is:
DataMapper::ImmutableError at /watch/1
Immutable resource cannot be lazy loaded

According to the official documentation:
Note that if you don't include the primary key in the selected columns, you will not be able to modify the returned resources because DataMapper cannot know how to persist them. DataMapper will raise DataMapper::ImmutableError if you're trying to do so nevertheless.
I know that you are not modifying anything here but I think that the same rule applies for lazy loading. So I will suggest to try it like that:
#results = Twitchtvst.all( :fields => [:Twitchtv, :id],
:conditions => { :user_id => "#{id}" }
) ode here
Note the id as an additional field.

Related

How to use .tap method to build associated record?

I have an omniauth authentication model I'm building that's associated to a user.
aka user has many authentications.
I wish to build up key-value pairs of this authentication models using tap because twitter provides a secret key while facebook does not.
So if I have this, I want to accomplish the following conditional statement using the .tap method instead.
class User < ActiveRecord::Base
def apply_omniauth(omni)
if omni['credentials']['secret']
self.authentications.build(:provider => omni['provider'],
:uid => omni['uid'],
:token => omni['credentials']['token'],
:token_secret => omni['credentials']['secret']
else
self.authentications.build(:provider => omni['provider'],
:uid => omni['uid'],
:token => omni['credentials']['token']
end
end
end
UPDATE:
I'm trying it this way. Does this accomplish the same as the above?
self.authentications.build.tap do |auth|
auth[:provider] = omni['provider'] if omni['provider']
auth[:uid] = omni['uid'] if omni['uid']
auth[:token] = omni['credentials']['token'] if omni['credentials']['token']
auth[:token_secret] = omni['credentials']['secret'] if omni['credentials']['secret']
end
I think you could simply do (self is obsolete here):
authentications.build(:provider => omni['provider'],
:uid => omni['uid'],
:token => omni['credentials']['token'],
:token_secret => omni['credentials']['secret'])
If any key is missing, this will simply assign nil value. Unless you have some custom logic for setting those attributes, there is no difference between assigning nil or not assigning anything for a new records.

Nested Document error on ES Tire Gem Ruby

I'm using the Tire gem still to work with a 0.9 ES database.
I've got a model that looks like this:
class Influencer
include Tire::Model::Persistence
include Tire::Model::DynamicPersistence
document_type "influencer"
# profiles
property :profiles, :type => 'nested', :class => [Profile], :default => []
end
I've also got a Profile class that looks like this.
class Profile
include Tire::Model::Persistence
include Tire::Model::DynamicPersistence
document_type 'profile'
property :id, :index => :not_analyzed
property :username, :index => :not_analyzed
property :service, :index => 'keyword'
property :url, :index => :not_analyzed
property :score, :type => 'float', :index => :not_analyzed
end
Note I removed many fields.
So, when I blow away the index and recreate it for influencers, I get an empty index. On the first write, it creates the mapping below:
{"influencer"=>
{"properties"=>
{"data"=>{"type"=>"object"},
"demographics"=>{"type"=>"object"},
"domain_names"=>{"type"=>"string"},
"geographics"=>{"type"=>"object"},
"host_names"=>{"type"=>"string"},
"id"=>{"type"=>"string"},
"income_message_numbers"=>{"type"=>"object"},
"influencer_id"=>{"type"=>"string"},
"keyword_scores"=>{"type"=>"object"},
"keywords"=>{"type"=>"string"},
"manual"=>{"type"=>"boolean"},
"name"=>{"type"=>"string"},
"notes"=>{"type"=>"object"},
"profiles"=>
{"properties"=>
{"profile"=>
{"properties"=>
{"contributors_enabled"=>{"type"=>"boolean"},
"default_profile"=>{"type"=>"boolean"},
"description"=>{"type"=>"string"},
"favourites_count"=>{"type"=>"long"},
"followers_count"=>{"type"=>"long"},
"friends_count"=>{"type"=>"long"},
"id"=>{"type"=>"long"},
"lang"=>{"type"=>"string"},
"listed_count"=>{"type"=>"long"},
"location"=>{"type"=>"string"},
"name"=>{"type"=>"string"},
"profile_image_url"=>{"type"=>"string"},
"protected"=>{"type"=>"boolean"},
"service"=>{"type"=>"string"},
"statuses_count"=>{"type"=>"long"},
"url"=>{"type"=>"string"},
"username"=>{"type"=>"string"}}}}},
"project_profiles"=>{"type"=>"object"},
"project_statuses"=>{"type"=>"object"},
"project_tags"=>{"type"=>"object"},
"project_time_additions"=>{"type"=>"object"},
"reachable_via"=>{"type"=>"string"},
"share_counts"=>{"type"=>"object"},
"source_ids"=>{"type"=>"string"},
"sources"=>{"type"=>"string"}}}}
This is what the first hash that writes looks like. I THINK it looks correct, but I could very well be missing something.
{:name=>"DogeWire", :profiles=>[{"service"=>"twitter", "id"=>2465530645, "name"=>"DogeWire", "username"=>"DogeWire", "description"=>"Fast, safe, anonymous, wow", "location"=>"www.DogeWire.com", "protected"=>false, "followers_count"=>466, "friends_count"=>1990, "listed_count"=>2, "favourites_count"=>19, "statuses_count"=>8421, "lang"=>"en", "contributors_enabled"=>false, "profile_image_url"=>"http://pbs.twimg.com/profile_images/460517818555846656/-0Ibxod4_normal.jpeg", "url"=>"http://twitter.com/DogeWire", "default_profile"=>true, "time_zone"=>nil}], :source_ids=>["twitter:2465530645"], :sources=>["twitter"], :keywords=>["altcoin"], :reachable_via=>["twitter"], :tags=>[], :host_names=>["www.lill.com"], :domain_names=>["lill.com"], :id=>"74uF9cNfW6dYEa27kSg5ww", :influencer_id=>"74uF9cNfW6dYEa27kSg5ww"}
And, lastly, the query looks like:
site_influencers = Influencer.search(:page => 1, :per_page => 10) do
query do
nested :path => 'profiles' do
query do
boolean do
must { match 'profiles.service', 'twitter' }
end
end
end
end
end
I'm not sure what i'm missing on this. This is a ruby app.. no Rails (but has ActiveRecord, etc).
Any thoughts?

Datamapper into String

I want to be able to see the string like the TwitchTV name I have in my database. Here is my current code
get '/watch/:id' do |id|
erb :mystream
#result = Twitchtvst.all( :fields => [:Twitchtv ],
:conditions => { :user_id => "#{id}" }
)
puts #result
end
result in terminal;
#< Twitchtvst:0x007fb48b4d5a98 >
How do I get that into a string (TwitchTV answer in database)
Opppppsss!
Here is the real code sample. Sorry!
get '/livestream' do
erb :livestream
#users_streams = Twitchtvst.all
puts #users_streams
end
If I add .to_s at users_stream it does not work
By adding .to_csv, not exactly a string, but it should show the content:
get '/livestream' do
erb :livestream
#users_streams = Twitchtvst.all
#users_streams.each do |us|
p us.to_csv
end
end
You're getting a Collection of Twitchtvst objects, so you need to convert each to a String:
puts Twitchtvst.all.map(&:to_s).join

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

How to union two different Mongoid Criteria

I have the following scopes defined in my model:
scope :upcoming, -> { where(:start_time.gt => Time.now).asc(:start_time) }
scope :in_progress, -> {
now = Time.now
where(:start_time.lte => now).where(:end_time.gte => now).asc(:start_time)
}
I want to create another scope that combines the results of both of those scopes called current. I tried something like this:
scope :current, -> { self.in_progress | self.upcoming }
But this just ends up treating them both like arrays and concatenating them. The problem with this is that when I try to call my scope with Model.current, I get the following error message:
NoMethodError: undefined method `as_conditions' for #<Array:0xaceb008>
This is because it converted the Mongoid Criteria object to an array, but I don't want that. I want the object to stay as a Mongoid Criteria object.
What I really want is the union of the in_progress set and the upcoming set.
Any ideas? Thanks.
You can try to compose your criteria using Mongoid's query methods and dereferencing into the criteria's selector, but I wouldn't necessarily recommend this -- see below for an example. I second the recommendation to craft your third scope. Remember that these scopes correspond to db queries that you want to be efficient, so it is probably worth your time to examine and understand the resulting and underlying MongoDB queries that are generated.
Model
class Episode
include Mongoid::Document
field :name, type: String
field :start_time, type: Time
field :end_time, type: Time
scope :upcoming, -> { where(:start_time.gt => Time.now).asc(:start_time) }
scope :in_progress, -> {
now = Time.now
where(:start_time.lte => now).where(:end_time.gte => now).asc(:start_time)
}
scope :current, -> { any_of([upcoming.selector, in_progress.selector]) }
scope :current_simpler, -> { where(:end_time.gte => Time.now) }
end
Test
require 'test_helper'
class EpisodeTest < ActiveSupport::TestCase
def setup
Episode.delete_all
end
test "scope composition" do
#p Episode.in_progress
#p Episode.upcoming
#p Episode.current
#p Episode.current_simpler
in_progress_name = 'In Progress'
upcoming_name = 'Upcoming'
Episode.create(:name => in_progress_name, :start_time => Time.now, :end_time => 1.hour.from_now)
Episode.create(:name => upcoming_name, :start_time => 1.hour.from_now, :end_time => 2.hours.from_now)
assert_equal([in_progress_name], Episode.in_progress.to_a.map(&:name))
assert_equal([upcoming_name], Episode.upcoming.to_a.map(&:name))
assert_equal([in_progress_name, upcoming_name], Episode.current.to_a.map(&:name))
assert_equal([in_progress_name, upcoming_name], Episode.current_simpler.to_a.map(&:name))
end
end
You have to map your Array back to a Mongoid::Criteria.
Any array of yours can be translated to a criteria with any_in:
scope :has_data, -> { any_in(:_id => all.select{ |record| record.data.size > 0 }.map{ |r| r.id }) }
So, something like this should do the trick: (untested)
scope :current, -> { any_in(:_id => (self.in_progress + self.upcoming).map{ |r| r.id }) }
I hope there exists better solutions, but this solves the equation at least.

Resources