Any examples of using Sinatra with acts_as_audited? - activerecord

background: I am building a web app using Sinatra and ActiveRecord and I am keen to take advantage of acts_as_audited (as per https://github.com/collectiveidea/acts_as_audited). The docs for acts_as_audited assume I'll be using Rails and so assume I'll use Rails to generate the necessary migrations. I've not found any examples of using acts_as_audited with Sinatra.
So my question: Can someone point me at an example of using Sinatra and ActiveRecord with acts_as_audited?

I have been able to get this to work using the Audit.as_user method. Using this method lets you audit records as if the change were made by the user object you pass in.
Here is a simple example.
# This is my User model, I want to audit email address changes to it.
class User < ActiveRecord::Base
acts_as_audited
# user has :email attribute
...
end
# This is what I would call in my Sinatra code.
# user is an instance of my User class
...
Audit.as_user(user) do
user.audit_comment = "updating email from sinatra"
user.update_attribute(:email, 'foo#bar.com')
end
...
A more complex example...
# Now I have a User model and a Comments model and I
# want to audit when I create a comment from Sinatra
class User < ActiveRecord::Base
has_many :comments
acts_as_audited
...
end
class Comment < ActiveRecord::Base
belongs_to :user
acts_as_audited
# has a :body attribute
...
end
# This is what I would call in my Sinatra code.
# Again, user is an instance of my User class
...
Audit.as_user(user) do
user.comments.create(
:body => "Body of Comment",
:audit_comment => "Creating Comment from Sinatra"
)
end

Related

Rails overriding active record setter in a relation

I want to override the << setter in my relation. For example, given:
class Library < ActiveRecord::Base
has_many :groups
def readers
groups.find_by(name: 'readers').users
end
end
class Group < ActiveRecord::Base
has_many :group_memberships
has_many :users, through: :group_memberships
end
class GroupMembership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :groups, through :group_membership
end
I want to do something like
someLibrary.readers << user1
and some additional things to happen after this.
The code should look something like:
def <<(objects)
super objects
#do other things here
end
Where should it be? I guess in Group, like:
class Group
...
def users<<(objects)
super objects
#do stuff
end
end
but I only want to do it when I'm invoking << on readers.
I want to know if there is a way to know whether I'm invoking << on a group users relationship, or whether I have access to group object when I'm invoking << method on group users through the relationship.
I want to do it because it looks nice. The easiest way would be to define separate method to set readers (and be more explicit), but I want to know if it is possible in activerecord or in ruby.
edit:
Yeah I know that overriding core methods is bad thing and people go to hell for that, yada yada yada.
I'm just curious how it's done. Like, for learning purposes.
Besides the aim is just to override the << method on that particular relation so probable there might be some justification why someone might want to do it.
Obligatory disclaimer:
I do not recommend that you do this, in 'important' code. Changing the behaviour of methods like this will confuse the hell out of other developers (as well as your future self), and lead to all sorts of unintended behavioural changes!
But assuming that this is 'just for fun'...
Based on the information above, someLibrary.readers returns a collection of User records. So all we need to do is add the desired behaviour to that class.
Normally you can do this by just defining a class method, in one of two ways:
class User
def self.foo
puts 'this works!'
end
class << self
def bar
puts 'this works too!'
end
end
end
With the above in place, you can call the methods like:
someLibrary.readers.foo
someLibrary.readers.bar
...However, there is some rails black magic going on under the hood here. someLibrary.readers is actually an instance of User::ActiveRecord_Associations_CollectionProxy, and the above methods are being picked up dynamically and appended to ActiveRecord::Associations::CollectionProxy.
Because of this dynamic method definition, it is not possible to override existing Rails methods (such as <<) in this manner. Instead, we'll need to monkey-patch the User::ActiveRecord_Associations_CollectionProxy class directly:
class User
class ActiveRecord_Associations_CollectionProxy
def <<(objects)
super(objects)
# do stuff
end
end
end
If you're looking for a better way of doing this however, I'd recommend using a service object design pattern. You can then encapsulate any more complex/custom logic relating to creating/updating/deleting users, libraries, etc. in a clean and isolated abstraction.
The more established way to do this...
class Library < ActiveRecord::Base
has_many :groups
has_one :reader_group -> {groups.find_by(name: 'readers')}
has_many :readers, through: :reader_group, class_name: 'User', foreign_key: 'user_id'
end
And that's it. You can now do
my_library.readers << another_user

Sinatra, ActiveRecord, FactoryGirl, and has_many_through associations

I am building a Sinatra API. My models use ActiveRecord and have a many-to-many model relationship.
class Workout < ActiveRecord::Base
has_many :workouts_exercises, dependent: :destroy
has_many :exercises, through: :workouts_exercises
end
class Exercise < ActiveRecord::Base
has_many :workouts_exercises
has_many :workouts, through: :workouts_exercises
end
class WorkoutsExercises < ActiveRecord::Base
belongs_to :workouts
belongs_to :exercises
end
I am trying to set up FactoryGirl to use these associations. Here is what I've got from reading all the docs I have found so far.
FactoryGirl.define do
factory :workout do
name 'Default Workout Factory Name'
description 'Default Workout Factory Description'
factory :workout_with_exercises do
after(:create) do |workout|
workout.exercises << FactoryGirl.create(:exercise)
end
end
end
end
FactoryGirl.define do
factory :exercise do
name 'Default Exercise Factory Name'
description 'Default Exercise Factory Description'
end
end
FactoryGirl.define do
factory :workouts_exercises do
workout
exercise
end
end
Here is my test that I would like to run
it 'returns the associated exercises' do
workout = FactoryGirl.create(:workout_with_exercises)
associated_exercises = workout.exercises
expect(associated_exercises.count).to eq(1)
end
However, when I run the specs I receive
1) Workout returns the associated exercises
Failure/Error: workout.exercises << FactoryGirl.create(:exercise)
NameError:
uninitialized constant Workout::WorkoutsExercise
The first method of debugging I tried was to pry before workout= declaration. Workout doesn't know about the exercises attribute. According to the ActiveRecord documentation, setting up a many-to-many association should provide you with the 16 #collection methods. This would mean that #exercises should return all the associated Exercise objects for the Workout object.
I can't, for the life of me, figure out whether or not it's my AR associations in the models that are to blame - or the FactoryGirl configuration I have. I've checked the SQL tables and all of the columns seem to be appropriate. I could really use some help figuring out what the issue is.
I've tried a few other FactoryGirl after_create hooks, using the shovel operator and attempting to declare the workouts_exercises association explicitly:
workout.exercises << [
create(:exercise, name: 'Bench Press', workouts_exercises: workout),
create(:exercise, name: 'Pushups', workouts_exercises: workout),
create(:exercise, name: 'DB Flys', workouts_exercises: workout)
]
Again, failure.
My third attempt was to use the FactoryGirl callback methods from ThoughtBot - ain't no calla back girl.
factory :workout_with_exercises do
after(:create) do |workout|
FactoryGirl.create(:exercise, workout: workout)
end
end
This results in
undefined method `exercise=' for #<Workout:0x007ff6250c2768>
Which makes me believe the AR associations aren't written correctly.
Thanks in advance for any help you can lend!
I use Sinatra, ActiveRecord, and RSpec. Just recently, I added FactoryGirl to the mix.
In your test framework's initialization point (I use RSpec, which is spec_helper.rb), you'll need to require the file that contains your model classes.
Next, do your model classes live within a module?
My model classes don't exist at the top-level, because I instead put them in a module for better organization within the project.
With this pattern, I found that I needed to explicitly define the module + class path for FactoryGirl.
The following would not work:
FactoryGirl.define do
factory :vendor do
name 'Test Vendor
end
end
But things began to work after I told FactoryGirl the full class name, like below:
FactoryGirl.define do
factory :vendor, class: MyAppName::Models::Vendor do
name 'Test Vendor
end
end

Many to many and one to many association between same models

I am creating a simple Sinatra app, using Sequel for my ORM.
Most of the data revolves around users and events where:
An event can have many users, one of which is the "owner".
Users can have many events, one or many of which they "own".
Here is a simplified version of my schema/model definitions:
class User < Sequel::Model(:users)
many_to_many :events
one_to_one :event
end
class Event < Sequel::Model(:events)
many_to_many :users
many_to_one :user
end
# provides a link between users and events
# e.g. event.users or user.events
# I am unsure how necessary this is :)
db.create_table :events_users do
primay_key :id
foreign_key :event_id, :events
foreign_key :user_id, :users
end
This allows me to get the users attached to an event, or the events that a user is attached to, but I am struggling to express the "ownership" of an event. It seems like the following pseudocode would work:
my_user = User.all.first
owned_events = Event.where(user_id = my_user.user_id)
That leads to two questions:
Does the current way i'm using assocations make sense?
How do I express ownership of an event in terms of Sequel's association model?
Maybe something like this:
class Event
many_to_one :owner, :class=>:User
many_to_many :users
end
class User
one_to_many :owned_events, :class=>:Event, :key=>:owner_id
many_to_many :events
end
You'll need to add owned_id field in events table.
Usage:
user = User.all.first
event = Event.new(:title => 'New Event')
events.add_owner(user)
event.save
another_user = User.create(:name => 'Jack')
event.add_user(another_user)

Rails 3 - Scopes

I have models set up similar to this:
class Book < ActiveRecord::Base
has_many :histories, as: :object
end
class Magazine < ActiveRecord::Base
has_many :histories, as: :object
end
class Action < ActiveRecord::Base
belongs_to :object, polymorphic: true
default_scope order(:done_at)
# a history contains an action and the time that action
# occurred on the object it belongs to
end
Now, I want to get a list of the 5 lastest actions that have occurred on all objects. So I can do something like:
Action.limit(5)
However, the problem is that if two actions have recently occurred on the same book, both actions will be retrieved. I want to only retrieve the lastest one. How do I achieve this?
Figured out that what I wanted was the group option. So I can do something like:
Action.group(:object_id).group(:object_type).limit(5)

Rails 3.1 - Binding HABTM from other controller

I have:
class Person < ActiveRecord::Base
has_many :people_phones
has_many :phones, :through => :people_phones
end
I also have:
class Request < ActiveRecord::Base
belongs_to :person
belongs_to :phone
end
Now when someone call with a request I open "requests#new" form, fill in person_id and phone_number and other details and submits them to "requests#create" controller#action.
In the "requests#create", I can do:
#phone = Phone.find_or_create_by_phone_number(params[:phone][:phone_number])
But how can I bind Person with that Phone from this Requests controller?
I mean create a record in people_phones table (if it doesn't exists)?
User.find(person_id).phones << #phone
I don't really know how your app works, but you see the idea.
If you have a request, and you want to "validate" it, you would do
request.person.phones << request.phone
Interesting stuff to know, it's kind of related (I'll try to find where I found that, it was a long time ago)
Steps required for the object to be saved to database:
New
Blog.new(…).save
user.blogs << Blog.new(…)
user.blogs.new(…).save – do not use, no practical use case
Build
Blog.build – not possible
user.blogs.build(…), user.save – both are required to save to DB
Create
Blog.create(…)
user.blogs.create(…)

Resources