1597669862
Understudy life is loaded up with endless difficulties. Some of the time they've to be wherever at once. Either it's tied in with submitting tasks, meeting with teachers, visiting the library, or dealing with their public activities. Remaining underneath the bend could lead them to the last position so they should be effective constantly.
The need or possibly want to remain immaculate all the time may deplete their imagination level. They likewise lose their ability for basic reasoning which produces their quality task papers.
Understudies need support and help. Just a dependable essay writer can assist them with conquering the progression of scholastic work by outfitting them with efficient task administrations. Proficient writers know the specialty of producing 100% one of a kind and counterfeiting free articles.
In spite of the fact that there are such a large number of online tricks that offer comparative types of assistance. You must be cautious while doling out your work to web based writing administrations. Since it ought to be great and ought to be a wellspring of expansion to your evaluations. Experience and surveys must be thought about while settling on any online organization.
Advantages Of Hiring Professional Writing Services
Understudies consistently search for three components when they scan for web based writing administrations and these significant things are:
Innovation
Idealness and
Moderateness
These are the most fundamental components that make criticalness. Proficient writers comprehend the estimation of value papers and they realize that understudies need to work under cutoff times. Organizations that give zero literary theft and never serve duplicated material ought to be employed for your write essay for me ventures. In this way, the administrations ought to be moderate for understudies, as they can not pay much for their tasks.
Deliberate Approach Towards Assignments
No big surprise that tasks need a deliberate methodology. Besides, best writing administrations consistently make a paper as per this methodology which incorporates:
Conceptualize the theme
Study your prerequisites
Examination about it
Gathers the information as indicated by the need of the paper
What's more, make it flawless according to your interest
Proficient writing administrations offer their mastery in:
Writing tasks
Mental
Clinical
The board
Report writing
Contextual investigation
Or on the other hand some other college, school or school task
You'll generally discover them remaining in front of your requests.
Copyright infringement free Essays and Academic Papers
Understudies consistently look for copyright infringement free substance for their write my essay, research papers, and any sort of scholarly writing task. Essay writing administrations that give unique and quality papers are the best.
Writing Services Who Are Centered On You
Best writing administrations don't rigging to lift up their name in the opposition yet rather raising your evaluations. They abstain from presenting the information that is underneath the standards and haven't been edited by their experts.
Custom Assignment Help
Proficient essay typer don't give the pre-fabricated format that cooks all broad task writing help. Truth be told, they give every task as indicated by the need or requests of the customer.
Offer Revision Requests
An update demand is the privilege of the customer. Understudies or scholastic experts who look for web based essay writing administrations and pay for their tasks anticipate the ideal papers. Pre-famous writing administrations consistently attempt to accomplish the customer's fulfillment and give them chief administrations.
Unconditional promise
Going through your cash online is hazardous and you should be careful about it. The enrolled and lawful organizations offer receipts of your exchanges. In the event that in the event that you're not happy with your task, at that point you can request a discount. They will discount your cash as indicated by the T&Cs of the organization which you should peruse before requesting the paper.
Uncommon Customer Support
To refresh the customer is one of the most fundamental highlights of any organization. Understudies or scholastic experts are a lot of worried about their writing ventures. They need ideal updates about their activities and some of the time they may need to refresh the organization also to give more directions.
Giving your college essay tasks in spare hands is absolutely your decision. However, it is without a doubt a superior method to handle such a large number of assignments one after another.
#essay
1615965721
With a lot of essay writing services online, finding a good and reliable task is not easy. I tried several different services and the one that worked best for me was getfinanceessay, but that was because I needed get finance paper.
1617949890
While you are in higher studies, the style of study and your course structure become different than ever. You will be evaluated in every step and will be prepared for the practical field. Essay writing is one of the major methods of evaluating a student’s learning and check out if he/she is ready for the next step.
You will be assigned by your professors to write essays and assignments on your topic and also will be guided by them. But, you have to do the entire thing as per your understanding. You need to analyze the topics, establish your points of view based on your research, and also state why your assignment and research work is new than the previous ones, and also provide enough reference and evidence the support your essays.
Now, usually the essays, you need to write at your graduation or post-graduation level, are 14000 to 15000 words. You may not get time to do such huge work while you already have the pressure of studies and other activities. Taking help from an Essay Writing Service can help you a lot in this and you can invest your time into something more important. But, why writing essays is so important in your higher studies? Read on to know more-
1. It Improves Your Writing Skill
Writing an essay means you have to make a structure of your essay, find required documents related to your topic, analyze those, and then write it down. It will help to improve your writing skills once you do it regularly. You also learn to organize your points and state those accordingly with references. It will help you to write your answers in the exam perfectly.
2. It Shows Your Understanding and Intelligence
The first line of your essay should show your strong grip over the topic. Once you finish your paper, it should be understood that it is well-researched and done by a person who has an explicit idea of the topic. If you want to gain professional knowledge and also make your writing skill better, you should write the essays perfectly.
3. Improve Your Research Skill
This is another importance of writing essays in higher studies. Without research, you can’t establish your points of view on the topic. While writing essays, you need to know what to research. First of all, you need to check out the previous works on it and find out what new you can add to those. Today, doing research is very easy on any topic. You can use the internet to find several information on the subject and then figure out which are the most relevant to use. Besides, there is not all the information available over the internet is not appropriate. So, you should not ignore your library work.
4. Make You Interested in Different Subjects
While researching your essay topic, you will find lots of information that may not directly involve your topic, but that can increase your interest in various other topics. You always get to learn something new when you conduct proper research for your essay and that’s why writing essays are important.
So, here you get the importance of writing essays. You can take help from Essay Help service Australia as they are the team of experts who can support you round-the-clock to get the best essays to be written and secure you get good score in the exam. Also, you can use your time to concentrate on your studies. Taking professional help will help you to submit the best quality essays within your deadline.
#essay writing service #essay help #essay writing help #essay writing #essay
1621931053
With a well written essay, students slowly tend to get a step closer to their dreams. Being largely aware of your needs, the Essay Writing Service solutions aim to put forward your academic priorities in the forefront.
The services are compiled with strict policies, norms and rules that are difficult for other companies to meet with. With a superior content quality and exceptional referencing, submitting award winning essays gets easier. To avail the services, keep reading to explore the top features and offerings.
Customized essay writing service:
LiveWebTutors is considered as one of the leading solutions that you would probably come across. With the zeal to offer an experienced essay help service, the scholars endeavour to offer their very best. With an amalgamation of their best knowledge, understanding and expertise, they curate solutions which are noteworthy. With our essay writing service, customized essays also gets a lot easier.
All you need to do is to offer your requirements and we have the team to do the rest for you. Make sure the specifications are clearly stated for the scholars to understand easily. The team of well appointed experts take a note of the requirements and venture into customizing the essays as per your preference.
A team of expert writers for you:
We have a large team of well chosen writers with an experience and expertise which you cannot get much of. Over the course of the professional service, we have searched and curated a team for ourselves that stands apart for its virtue and diligence. This is why we have a large team of students consulting us for essay help.
The writers in our team are dedicated to offer a trusted essay writing service and share a professional expertise that beats all odds. We help you achieve an academic grade which is truly worthy. Hence, with added flexibility and convenience, we let you choose a writer for yourself and entrust them with the sole responsibility of writing a well formulated essay.
Get free revisions with every order:
In only a very few cases, the essays might come up with a few problems and can get turned back as they might have not been written as per the requirements or expectations of the students. This is when you can request for a revision and get your requirements fulfilled.
Attaining our essay writing help is as easy as that! Whenever you wish to get free revisions, you must reach out to us to attain as many free revisions as you want. This is what makes us worthy of offering a reliable essay help, with the experts offering unlimited revisions as per your requests. Moreover, we offer this at no additional cost.
Get the following benefits with our essay writing help:
• Get a high quality essay
• 100% plagiarism free essay
• Trusted round the clock essay writing help
• PhD qualified tutors
• Online tutoring session
• Unlimited revisions
• Get guaranteed refund
• 100% confidential service
• Trusted online transactions
• Easy and hassle free online essay help
Hire the essay pros today:
At LiveWebTutors, our main motto is to enable students to largely excel in their academic career. This is why we put forward the best essay writing help solutions to help students. We have a rich experience in every single academic field and deliver an error free essay in no time.
Hence, what are you waiting for! Simple visit our website today to seek help from the experts. Venture towards a prospective academic career with us; reach out to us at the earliest to receive the help you need.
#essay writing service #essay help #essay writing help #essay writing #essay
1623850079
If your essay worked - getting someone to “do my homework for me” was probably the least difficult part of the whole world! All you need is an excellent, passionate team of remarkably skilled English-speaking writers ready for your call. This is what we grew up at WriteMyEssay! Using our highly selective candidate selection process, we have built a diverse and skilled team of writers who are effective in tackling any educational challenge. What’s more, they actually make it easy, as well as a price that doesn’t burn a hole in your budget!
Hence, you are currently one of the many students who scream in despair: please remember to write my essay before it is too late? »Guess what’s going on: it’s never too late! Contact our 24/7 customer service office at WriteMyEssay now and you’ll be ready for any assignment, no matter how large or small. We value customer joy, useful assistance, and skilled writers right from the start, which means you know we’re number 1 in the writing business.
Contact us right now and you will find out first-hand what kind of product quality assurance work we offer you!
#essay #essay paper #essay papers #student #write my essay #custom essay
1627059964
Students who are invested in learning do not require essay writing or assignment services for their school work. So if you are someone who wants to become a good student then here are some tips for you:-
1. Do advance study
Good students always have a habit of staying ahead of the class. They revise what is taught on the day and prepare for the lectures that are coming up. Make a timetable and follow it thoroughly. Make a habit of studying every day’s lesson without failure. This is a good habit as you do not pile up topics for you’re before an exam. Meanwhile, if it gets challenging to handle school work and assignments, you can get a business law case study or other assignment services for school work.
2. Participate in school activities
School life is something which you will not get again. So instead of just focusing on studies, you should participate in other activities too. You can follow your passion and skills to improve them. There are certificates received for participation, too, which can help you get scholarships or be added to your resume for the job hunt. While you are busy participating in events, you can get philosophy essay help, finance help etc., for your school tasks.
3. Make good friends
Students often wonder who can write my essay? But good students have a solution to it by staying in groups with their finds. Having good friends in school is essential. They can help you develop good skills, inspire you and lead you to better opportunities.
4. Think about future
Wise students always have a plan for their future. Some students even start to plan for their future from high school itself. Focus on the subjects you want to master and gain skills that will be advantageous for your career. A strategic approach can help you build the foundation you wish to for a good life ahead.
These are some of the ways on how to become a good student.
Other Source:
URL Reference: https://drt.wowlaunch.com/users/blog/6006480/1954451/four-tips-on-how-to-be-a-good-student/
#write my essay #essay writing #business law case study #philosophy essay help #mba essay help
1659283860
ActiveInteraction manages application-specific business logic. It's an implementation of service objects designed to blend seamlessly into Rails.
ActiveInteraction gives you a place to put your business logic. It also helps you write safer code by validating that your inputs conform to your expectations. If ActiveModel deals with your nouns, then ActiveInteraction handles your verbs.
Add it to your Gemfile:
gem 'active_interaction', '~> 5.1'
Or install it manually:
$ gem install active_interaction --version '~> 5.1'
This project uses Semantic Versioning. Check out GitHub releases for a detailed list of changes.
To define an interaction, create a subclass of ActiveInteraction::Base
. Then you need to do two things:
Define your inputs. Use class filter methods to define what you expect your inputs to look like. For instance, if you need a boolean flag for pepperoni, use boolean :pepperoni
. Check out the filters section for all the available options.
Define your business logic. Do this by implementing the #execute
method. Each input you defined will be available as the type you specified. If any of the inputs are invalid, #execute
won't be run. Filters are responsible for checking your inputs. Check out the validations section if you need more than that.
That covers the basics. Let's put it all together into a simple example that squares a number.
require 'active_interaction'
class Square < ActiveInteraction::Base
float :x
def execute
x**2
end
end
Call .run
on your interaction to execute it. You must pass a single hash to .run
. It will return an instance of your interaction. By convention, we call this an outcome. You can use the #valid?
method to ask the outcome if it's valid. If it's invalid, take a look at its errors with #errors
. In either case, the value returned from #execute
will be stored in #result
.
outcome = Square.run(x: 'two point one')
outcome.valid?
# => nil
outcome.errors.messages
# => {:x=>["is not a valid float"]}
outcome = Square.run(x: 2.1)
outcome.valid?
# => true
outcome.result
# => 4.41
You can also use .run!
to execute interactions. It's like .run
but more dangerous. It doesn't return an outcome. If the outcome would be invalid, it will instead raise an error. But if the outcome would be valid, it simply returns the result.
Square.run!(x: 'two point one')
# ActiveInteraction::InvalidInteractionError: X is not a valid float
Square.run!(x: 2.1)
# => 4.41
ActiveInteraction checks your inputs. Often you'll want more than that. For instance, you may want an input to be a string with at least one non-whitespace character. Instead of writing your own validation for that, you can use validations from ActiveModel.
These validations aren't provided by ActiveInteraction. They're from ActiveModel. You can also use any custom validations you wrote yourself in your interactions.
class SayHello < ActiveInteraction::Base
string :name
validates :name,
presence: true
def execute
"Hello, #{name}!"
end
end
When you run this interaction, two things will happen. First ActiveInteraction will check your inputs. Then ActiveModel will validate them. If both of those are happy, it will be executed.
SayHello.run!(name: nil)
# ActiveInteraction::InvalidInteractionError: Name is required
SayHello.run!(name: '')
# ActiveInteraction::InvalidInteractionError: Name can't be blank
SayHello.run!(name: 'Taylor')
# => "Hello, Taylor!"
You can define filters inside an interaction using the appropriate class method. Each method has the same signature:
Some symbolic names. These are the attributes to create.
An optional hash of options. Each filter supports at least these two options:
default
is the fallback value to use if nil
is given. To make a filter optional, set default: nil
.
desc
is a human-readable description of the input. This can be useful for generating documentation. For more information about this, read the descriptions section.
An optional block of sub-filters. Only array and hash filters support this. Other filters will ignore blocks when given to them.
Let's take a look at an example filter. It defines three inputs: x
, y
, and z
. Those inputs are optional and they all share the same description ("an example filter").
array :x, :y, :z,
default: nil,
desc: 'an example filter' do
# Some filters support sub-filters here.
end
In general, filters accept values of the type they correspond to, plus a few alternatives that can be reasonably coerced. Typically the coercions come from Rails, so "1"
can be interpreted as the boolean value true
, the string "1"
, or the number 1
.
In addition to accepting arrays, array inputs will convert ActiveRecord::Relation
s into arrays.
class ArrayInteraction < ActiveInteraction::Base
array :toppings
def execute
toppings.size
end
end
ArrayInteraction.run!(toppings: 'everything')
# ActiveInteraction::InvalidInteractionError: Toppings is not a valid array
ArrayInteraction.run!(toppings: [:cheese, 'pepperoni'])
# => 2
Use a block to constrain the types of elements an array can contain. Note that you can only have one filter inside an array block, and it must not have a name.
array :birthdays do
date
end
For interface
, object
, and record
filters, the name of the array filter will be singularized and used to determine the type of value passed. In the example below, the objects passed would need to be of type Cow
.
array :cows do
object
end
You can override this by passing the necessary information to the inner filter.
array :managers do
object class: People
end
Errors that occur will be indexed based on the Rails configuration setting index_nested_attribute_errors
. You can also manually override this setting with the :index_errors
option. In this state is is possible to get multiple errors from a single filter.
class ArrayInteraction < ActiveInteraction::Base
array :favorite_numbers, index_errors: true do
integer
end
def execute
favorite_numbers
end
end
ArrayInteraction.run(favorite_numbers: [8, 'bazillion']).errors.details
=> {:"favorite_numbers[1]"=>[{:error=>:invalid_type, :type=>"array"}]}
With :index_errors
set to false
the error would have been:
{:favorite_numbers=>[{:error=>:invalid_type, :type=>"array"}]}
Boolean filters convert the strings "1"
, "true"
, and "on"
(case-insensitive) into true
. They also convert "0"
, "false"
, and "off"
into false
. Blank strings will be treated as nil
.
class BooleanInteraction < ActiveInteraction::Base
boolean :kool_aid
def execute
'Oh yeah!' if kool_aid
end
end
BooleanInteraction.run!(kool_aid: 1)
# ActiveInteraction::InvalidInteractionError: Kool aid is not a valid boolean
BooleanInteraction.run!(kool_aid: true)
# => "Oh yeah!"
File filters also accept TempFile
s and anything that responds to #rewind
. That means that you can pass the params
from uploading files via forms in Rails.
class FileInteraction < ActiveInteraction::Base
file :readme
def execute
readme.size
end
end
FileInteraction.run!(readme: 'README.md')
# ActiveInteraction::InvalidInteractionError: Readme is not a valid file
FileInteraction.run!(readme: File.open('README.md'))
# => 21563
Hash filters accept hashes. The expected value types are given by passing a block and nesting other filters. You can have any number of filters inside a hash, including other hashes.
class HashInteraction < ActiveInteraction::Base
hash :preferences do
boolean :newsletter
boolean :sweepstakes
end
def execute
puts 'Thanks for joining the newsletter!' if preferences[:newsletter]
puts 'Good luck in the sweepstakes!' if preferences[:sweepstakes]
end
end
HashInteraction.run!(preferences: 'yes, no')
# ActiveInteraction::InvalidInteractionError: Preferences is not a valid hash
HashInteraction.run!(preferences: { newsletter: true, 'sweepstakes' => false })
# Thanks for joining the newsletter!
# => nil
Setting default hash values can be tricky. The default value has to be either nil
or {}
. Use nil
to make the hash optional. Use {}
if you want to set some defaults for values inside the hash.
hash :optional,
default: nil
# => {:optional=>nil}
hash :with_defaults,
default: {} do
boolean :likes_cookies,
default: true
end
# => {:with_defaults=>{:likes_cookies=>true}}
By default, hashes remove any keys that aren't given as nested filters. To allow all hash keys, set strip: false
. In general we don't recommend doing this, but it's sometimes necessary.
hash :stuff,
strip: false
String filters define inputs that only accept strings.
class StringInteraction < ActiveInteraction::Base
string :name
def execute
"Hello, #{name}!"
end
end
StringInteraction.run!(name: 0xDEADBEEF)
# ActiveInteraction::InvalidInteractionError: Name is not a valid string
StringInteraction.run!(name: 'Taylor')
# => "Hello, Taylor!"
String filter strips leading and trailing whitespace by default. To disable it, set the strip
option to false
.
string :comment,
strip: false
Symbol filters define inputs that accept symbols. Strings will be converted into symbols.
class SymbolInteraction < ActiveInteraction::Base
symbol :method
def execute
method.to_proc
end
end
SymbolInteraction.run!(method: -> {})
# ActiveInteraction::InvalidInteractionError: Method is not a valid symbol
SymbolInteraction.run!(method: :object_id)
# => #<Proc:0x007fdc9ba94118>
Filters that work with dates and times behave similarly. By default, they all convert strings into their expected data types using .parse
. Blank strings will be treated as nil
. If you give the format
option, they will instead convert strings using .strptime
. Note that formats won't work with DateTime
and Time
filters if a time zone is set.
Date
class DateInteraction < ActiveInteraction::Base
date :birthday
def execute
birthday + (18 * 365)
end
end
DateInteraction.run!(birthday: 'yesterday')
# ActiveInteraction::InvalidInteractionError: Birthday is not a valid date
DateInteraction.run!(birthday: Date.new(1989, 9, 1))
# => #<Date: 2007-08-28 ((2454341j,0s,0n),+0s,2299161j)>
date :birthday,
format: '%Y-%m-%d'
DateTime
class DateTimeInteraction < ActiveInteraction::Base
date_time :now
def execute
now.iso8601
end
end
DateTimeInteraction.run!(now: 'now')
# ActiveInteraction::InvalidInteractionError: Now is not a valid date time
DateTimeInteraction.run!(now: DateTime.now)
# => "2015-03-11T11:04:40-05:00"
date_time :start,
format: '%Y-%m-%dT%H:%M:%S'
Time
In addition to converting strings with .parse
(or .strptime
), time filters convert numbers with .at
.
class TimeInteraction < ActiveInteraction::Base
time :epoch
def execute
Time.now - epoch
end
end
TimeInteraction.run!(epoch: 'a long, long time ago')
# ActiveInteraction::InvalidInteractionError: Epoch is not a valid time
TimeInteraction.run!(epoch: Time.new(1970))
# => 1426068362.5136619
time :start,
format: '%Y-%m-%dT%H:%M:%S'
All numeric filters accept numeric input. They will also convert strings using the appropriate method from Kernel
(like .Float
). Blank strings will be treated as nil
.
Decimal
class DecimalInteraction < ActiveInteraction::Base
decimal :price
def execute
price * 1.0825
end
end
DecimalInteraction.run!(price: 'one ninety-nine')
# ActiveInteraction::InvalidInteractionError: Price is not a valid decimal
DecimalInteraction.run!(price: BigDecimal(1.99, 2))
# => #<BigDecimal:7fe792a42028,'0.2165E1',18(45)>
To specify the number of significant digits, use the digits
option.
decimal :dollars,
digits: 2
Float
class FloatInteraction < ActiveInteraction::Base
float :x
def execute
x**2
end
end
FloatInteraction.run!(x: 'two point one')
# ActiveInteraction::InvalidInteractionError: X is not a valid float
FloatInteraction.run!(x: 2.1)
# => 4.41
Integer
class IntegerInteraction < ActiveInteraction::Base
integer :limit
def execute
limit.downto(0).to_a
end
end
IntegerInteraction.run!(limit: 'ten')
# ActiveInteraction::InvalidInteractionError: Limit is not a valid integer
IntegerInteraction.run!(limit: 10)
# => [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
When a String
is passed into an integer
input, the value will be coerced. A default base of 10
is used though it may be overridden with the base
option. If a base of 0
is provided, the coercion will respect radix indicators present in the string.
class IntegerInteraction < ActiveInteraction::Base
integer :limit1
integer :limit2, base: 8
integer :limit3, base: 0
def execute
[limit1, limit2, limit3]
end
end
IntegerInteraction.run!(limit1: 71, limit2: 71, limit3: 71)
# => [71, 71, 71]
IntegerInteraction.run!(limit1: "071", limit2: "071", limit3: "0x71")
# => [71, 57, 113]
IntegerInteraction.run!(limit1: "08", limit2: "08", limit3: "08")
ActiveInteraction::InvalidInteractionError: Limit2 is not a valid integer, Limit3 is not a valid integer
Interface filters allow you to specify an interface that the passed value must meet in order to pass. The name of the interface is used to look for a constant inside the ancestor listing for the passed value. This allows for a variety of checks depending on what's passed. Class instances are checked for an included module or an inherited ancestor class. Classes are checked for an extended module or an inherited ancestor class. Modules are checked for an extended module.
class InterfaceInteraction < ActiveInteraction::Base
interface :exception
def execute
exception
end
end
InterfaceInteraction.run!(exception: Exception)
# ActiveInteraction::InvalidInteractionError: Exception is not a valid interface
InterfaceInteraction.run!(exception: NameError) # a subclass of Exception
# => NameError
You can use :from
to specify a class or module. This would be the equivalent of what's above.
class InterfaceInteraction < ActiveInteraction::Base
interface :error,
from: Exception
def execute
error
end
end
You can also create an anonymous interface on the fly by passing the methods
option.
class InterfaceInteraction < ActiveInteraction::Base
interface :serializer,
methods: %i[dump load]
def execute
input = '{ "is_json" : true }'
object = serializer.load(input)
output = serializer.dump(object)
output
end
end
require 'json'
InterfaceInteraction.run!(serializer: Object.new)
# ActiveInteraction::InvalidInteractionError: Serializer is not a valid interface
InterfaceInteraction.run!(serializer: JSON)
# => "{\"is_json\":true}"
Object filters allow you to require an instance of a particular class or one of its subclasses.
class Cow
def moo
'Moo!'
end
end
class ObjectInteraction < ActiveInteraction::Base
object :cow
def execute
cow.moo
end
end
ObjectInteraction.run!(cow: Object.new)
# ActiveInteraction::InvalidInteractionError: Cow is not a valid object
ObjectInteraction.run!(cow: Cow.new)
# => "Moo!"
The class name is automatically determined by the filter name. If your filter name is different than your class name, use the class
option. It can be either the class, a string, or a symbol.
object :dolly1,
class: Sheep
object :dolly2,
class: 'Sheep'
object :dolly3,
class: :Sheep
If you have value objects or you would like to build one object from another, you can use the converter
option. It is only called if the value provided is not an instance of the class or one of its subclasses. The converter
option accepts a symbol that specifies a class method on the object class or a proc. Both will be passed the value and any errors thrown inside the converter will cause the value to be considered invalid. Any returned value that is not the correct class will also be treated as invalid. Any default
that is not an instance of the class or subclass and is not nil
will also be converted.
class ObjectInteraction < ActiveInteraction::Base
object :ip_address,
class: IPAddr,
converter: :new
def execute
ip_address
end
end
ObjectInteraction.run!(ip_address: '192.168.1.1')
# #<IPAddr: IPv4:192.168.1.1/255.255.255.255>
ObjectInteraction.run!(ip_address: 1)
# ActiveInteraction::InvalidInteractionError: Ip address is not a valid object
Record filters allow you to require an instance of a particular class (or one of its subclasses) or a value that can be used to locate an instance of the object. If the value does not match, it will call find
on the class of the record. This is particularly useful when working with ActiveRecord objects. Like an object filter, the class is derived from the name passed but can be specified with the class
option. Any default
that is not an instance of the class or subclass and is not nil
will also be found. Blank strings passed in will be treated as nil
.
class RecordInteraction < ActiveInteraction::Base
record :encoding
def execute
encoding
end
end
> RecordInteraction.run!(encoding: Encoding::US_ASCII)
=> #<Encoding:US-ASCII>
> RecordInteraction.run!(encoding: 'ascii')
=> #<Encoding:US-ASCII>
A different method can be specified by providing a symbol to the finder
option.
ActiveInteraction plays nicely with Rails. You can use interactions to handle your business logic instead of models or controllers. To see how it all works, let's take a look at a complete example of a controller with the typical resourceful actions.
We recommend putting your interactions in app/interactions
. It's also very helpful to group them by model. That way you can look in app/interactions/accounts
for all the ways you can interact with accounts.
- app/
- controllers/
- accounts_controller.rb
- interactions/
- accounts/
- create_account.rb
- destroy_account.rb
- find_account.rb
- list_accounts.rb
- update_account.rb
- models/
- account.rb
- views/
- account/
- edit.html.erb
- index.html.erb
- new.html.erb
- show.html.erb
# GET /accounts
def index
@accounts = ListAccounts.run!
end
Since we're not passing any inputs to ListAccounts
, it makes sense to use .run!
instead of .run
. If it failed, that would mean we probably messed up writing the interaction.
class ListAccounts < ActiveInteraction::Base
def execute
Account.not_deleted.order(last_name: :asc, first_name: :asc)
end
end
Up next is the show action. For this one we'll define a helper method to handle raising the correct errors. We have to do this because calling .run!
would raise an ActiveInteraction::InvalidInteractionError
instead of an ActiveRecord::RecordNotFound
. That means Rails would render a 500 instead of a 404.
# GET /accounts/:id
def show
@account = find_account!
end
private
def find_account!
outcome = FindAccount.run(params)
if outcome.valid?
outcome.result
else
fail ActiveRecord::RecordNotFound, outcome.errors.full_messages.to_sentence
end
end
This probably looks a little different than you're used to. Rails commonly handles this with a before_filter
that sets the @account
instance variable. Why is all this interaction code better? Two reasons: One, you can reuse the FindAccount
interaction in other places, like your API controller or a Resque task. And two, if you want to change how accounts are found, you only have to change one place.
Inside the interaction, we could use #find
instead of #find_by_id
. That way we wouldn't need the #find_account!
helper method in the controller because the error would bubble all the way up. However, you should try to avoid raising errors from interactions. If you do, you'll have to deal with raised exceptions as well as the validity of the outcome.
class FindAccount < ActiveInteraction::Base
integer :id
def execute
account = Account.not_deleted.find_by_id(id)
if account
account
else
errors.add(:id, 'does not exist')
end
end
end
Note that it's perfectly fine to add errors during execution. Not all errors have to come from checking or validation.
The new action will be a little different than the ones we've looked at so far. Instead of calling .run
or .run!
, it's going to initialize a new interaction. This is possible because interactions behave like ActiveModels.
# GET /accounts/new
def new
@account = CreateAccount.new
end
Since interactions behave like ActiveModels, we can use ActiveModel validations with them. We'll use validations here to make sure that the first and last names are not blank. The validations section goes into more detail about this.
class CreateAccount < ActiveInteraction::Base
string :first_name, :last_name
validates :first_name, :last_name,
presence: true
def to_model
Account.new
end
def execute
account = Account.new(inputs)
unless account.save
errors.merge!(account.errors)
end
account
end
end
We used a couple of advanced features here. The #to_model
method helps determine the correct form to use in the view. Check out the section on forms for more about that. Inside #execute
, we merge errors. This is a convenient way to move errors from one object to another. Read more about it in the errors section.
The create action has a lot in common with the new action. Both of them use the CreateAccount
interaction. And if creating the account fails, this action falls back to rendering the new action.
# POST /accounts
def create
outcome = CreateAccount.run(params.fetch(:account, {}))
if outcome.valid?
redirect_to(outcome.result)
else
@account = outcome
render(:new)
end
end
Note that we have to pass a hash to .run
. Passing nil
is an error.
Since we're using an interaction, we don't need strong parameters. The interaction will ignore any inputs that weren't defined by filters. So you can forget about params.require
and params.permit
because interactions handle that for you.
The destroy action will reuse the #find_account!
helper method we wrote earlier.
# DELETE /accounts/:id
def destroy
DestroyAccount.run!(account: find_account!)
redirect_to(accounts_url)
end
In this simple example, the destroy interaction doesn't do much. It's not clear that you gain anything by putting it in an interaction. But in the future, when you need to do more than account.destroy
, you'll only have to update one spot.
class DestroyAccount < ActiveInteraction::Base
object :account
def execute
account.destroy
end
end
Just like the destroy action, editing uses the #find_account!
helper. Then it creates a new interaction instance to use as a form object.
# GET /accounts/:id/edit
def edit
account = find_account!
@account = UpdateAccount.new(
account: account,
first_name: account.first_name,
last_name: account.last_name)
end
The interaction that updates accounts is more complicated than the others. It requires an account to update, but the other inputs are optional. If they're missing, it'll ignore those attributes. If they're present, it'll update them.
class UpdateAccount < ActiveInteraction::Base
object :account
string :first_name, :last_name,
default: nil
validates :first_name,
presence: true,
unless: -> { first_name.nil? }
validates :last_name,
presence: true,
unless: -> { last_name.nil? }
def execute
account.first_name = first_name if first_name.present?
account.last_name = last_name if last_name.present?
unless account.save
errors.merge!(account.errors)
end
account
end
end
Hopefully you've gotten the hang of this by now. We'll use #find_account!
to get the account. Then we'll build up the inputs for UpdateAccount
. Then we'll run the interaction and either redirect to the updated account or back to the edit page.
# PUT /accounts/:id
def update
inputs = { account: find_account! }.reverse_merge(params[:account])
outcome = UpdateAccount.run(inputs)
if outcome.valid?
redirect_to(outcome.result)
else
@account = outcome
render(:edit)
end
end
ActiveSupport::Callbacks provides a powerful framework for defining callbacks. ActiveInteraction uses that framework to allow hooking into various parts of an interaction's lifecycle.
class Increment < ActiveInteraction::Base
set_callback :filter, :before, -> { puts 'before filter' }
integer :x
set_callback :validate, :after, -> { puts 'after validate' }
validates :x,
numericality: { greater_than_or_equal_to: 0 }
set_callback :execute, :around, lambda { |_interaction, block|
puts '>>>'
block.call
puts '<<<'
}
def execute
puts 'executing'
x + 1
end
end
Increment.run!(x: 1)
# before filter
# after validate
# >>>
# executing
# <<<
# => 2
In order, the available callbacks are filter
, validate
, and execute
. You can set before
, after
, or around
on any of them.
You can run interactions from within other interactions with #compose
. If the interaction is successful, it'll return the result (just like if you had called it with .run!
). If something went wrong, execution will halt immediately and the errors will be moved onto the caller.
class Add < ActiveInteraction::Base
integer :x, :y
def execute
x + y
end
end
class AddThree < ActiveInteraction::Base
integer :x
def execute
compose(Add, x: x, y: 3)
end
end
AddThree.run!(x: 5)
# => 8
To bring in filters from another interaction, use .import_filters
. Combined with inputs
, delegating to another interaction is a piece of cake.
class AddAndDouble < ActiveInteraction::Base
import_filters Add
def execute
compose(Add, inputs) * 2
end
end
Note that errors in composed interactions have a few tricky cases. See the errors section for more information about them.
The default value for an input can take on many different forms. Setting the default to nil
makes the input optional. Setting it to some value makes that the default value for that input. Setting it to a lambda will lazily set the default value for that input. That means the value will be computed when the interaction is run, as opposed to when it is defined.
Lambda defaults are evaluated in the context of the interaction, so you can use the values of other inputs in them.
# This input is optional.
time :a, default: nil
# This input defaults to `Time.at(123)`.
time :b, default: Time.at(123)
# This input lazily defaults to `Time.now`.
time :c, default: -> { Time.now }
# This input defaults to the value of `c` plus 10 seconds.
time :d, default: -> { c + 10 }
Use the desc
option to provide human-readable descriptions of filters. You should prefer these to comments because they can be used to generate documentation. The interaction class has a .filters
method that returns a hash of filters. Each filter has a #desc
method that returns the description.
class Descriptive < ActiveInteraction::Base
string :first_name,
desc: 'your first name'
string :last_name,
desc: 'your last name'
end
Descriptive.filters.each do |name, filter|
puts "#{name}: #{filter.desc}"
end
# first_name: your first name
# last_name: your last name
ActiveInteraction provides detailed errors for easier introspection and testing of errors. Detailed errors improve on regular errors by adding a symbol that represents the type of error that has occurred. Let's look at an example where an item is purchased using a credit card.
class BuyItem < ActiveInteraction::Base
object :credit_card, :item
hash :options do
boolean :gift_wrapped
end
def execute
order = credit_card.purchase(item)
notify(credit_card.account)
order
end
private def notify(account)
# ...
end
end
Having missing or invalid inputs causes the interaction to fail and return errors.
outcome = BuyItem.run(item: 'Thing', options: { gift_wrapped: 'yes' })
outcome.errors.messages
# => {:credit_card=>["is required"], :item=>["is not a valid object"], :"options.gift_wrapped"=>["is not a valid boolean"]}
Determining the type of error based on the string is difficult if not impossible. Calling #details
instead of #messages
on errors
gives you the same list of errors with a testable label representing the error.
outcome.errors.details
# => {:credit_card=>[{:error=>:missing}], :item=>[{:error=>:invalid_type, :type=>"object"}], :"options.gift_wrapped"=>[{:error=>:invalid_type, :type=>"boolean"}]}
Detailed errors can also be manually added during the execute call by passing a symbol to #add
instead of a string.
def execute
errors.add(:monster, :no_passage)
end
ActiveInteraction also supports merging errors. This is useful if you want to delegate validation to some other object. For example, if you have an interaction that updates a record, you might want that record to validate itself. By using the #merge!
helper on errors
, you can do exactly that.
class UpdateThing < ActiveInteraction::Base
object :thing
def execute
unless thing.save
errors.merge!(thing.errors)
end
thing
end
end
When a composed interaction fails, its errors are merged onto the caller. This generally produces good error messages, but there are a few cases to look out for.
class Inner < ActiveInteraction::Base
boolean :x, :y
end
class Outer < ActiveInteraction::Base
string :x
boolean :z, default: nil
def execute
compose(Inner, x: x, y: z)
end
end
outcome = Outer.run(x: 'yes')
outcome.errors.details
# => { :x => [{ :error => :invalid_type, :type => "boolean" }],
# :base => [{ :error => "Y is required" }] }
outcome.errors.full_messages.join(' and ')
# => "X is not a valid boolean and Y is required"
Since both interactions have an input called x
, the inner error for that input is moved to the x
error on the outer interaction. This results in a misleading error that claims the input x
is not a valid boolean even though it's a string on the outer interaction.
Since only the inner interaction has an input called y
, the inner error for that input is moved to the base
error on the outer interaction. This results in a confusing error that claims the input y
is required even though it's not present on the outer interaction.
The outcome returned by .run
can be used in forms as though it were an ActiveModel object. You can also create a form object by calling .new
on the interaction.
Given an application with an Account
model we'll create a new Account
using the CreateAccount
interaction.
# GET /accounts/new
def new
@account = CreateAccount.new
end
# POST /accounts
def create
outcome = CreateAccount.run(params.fetch(:account, {}))
if outcome.valid?
redirect_to(outcome.result)
else
@account = outcome
render(:new)
end
end
The form used to create a new Account
has slightly more information on the form_for
call than you might expect.
<%= form_for @account, as: :account, url: accounts_path do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.submit 'Create' %>
<% end %>
This is necessary because we want the form to act like it is creating a new Account
. Defining to_model
on the CreateAccount
interaction tells the form to treat our interaction like an Account
.
class CreateAccount < ActiveInteraction::Base
# ...
def to_model
Account.new
end
end
Now our form_for
call knows how to generate the correct URL and param name (i.e. params[:account]
).
# app/views/accounts/new.html.erb
<%= form_for @account do |f| %>
<%# ... %>
<% end %>
If you have an interaction that updates an Account
, you can define to_model
to return the object you're updating.
class UpdateAccount < ActiveInteraction::Base
# ...
object :account
def to_model
account
end
end
ActiveInteraction also supports formtastic and simple_form. The filters used to define the inputs on your interaction will relay type information to these gems. As a result, form fields will automatically use the appropriate input type.
It can be convenient to apply the same options to a bunch of inputs. One common use case is making many inputs optional. Instead of setting default: nil
on each one of them, you can use with_options
to reduce duplication.
with_options default: nil do
date :birthday
string :name
boolean :wants_cake
end
Optional inputs can be defined by using the :default
option as described in the filters section. Within the interaction, provided and default values are merged to create inputs
. There are times where it is useful to know whether a value was passed to run
or the result of a filter default. In particular, it is useful when nil
is an acceptable value. For example, you may optionally track your users' birthdays. You can use the inputs.given?
predicate to see if an input was even passed to run
. With inputs.given?
you can also check the input of a hash or array filter by passing a series of keys or indexes to check.
class UpdateUser < ActiveInteraction::Base
object :user
date :birthday,
default: nil
def execute
user.birthday = birthday if inputs.given?(:birthday)
errors.merge!(user.errors) unless user.save
user
end
end
Now you have a few options. If you don't want to update their birthday, leave it out of the hash. If you want to remove their birthday, set birthday: nil
. And if you want to update it, pass in the new value as usual.
user = User.find(...)
# Don't update their birthday.
UpdateUser.run!(user: user)
# Remove their birthday.
UpdateUser.run!(user: user, birthday: nil)
# Update their birthday.
UpdateUser.run!(user: user, birthday: Date.new(2000, 1, 2))
ActiveInteraction is i18n aware out of the box! All you have to do is add translations to your project. In Rails, these typically go into config/locales
. For example, let's say that for some reason you want to print everything out backwards. Simply add translations for ActiveInteraction to your hsilgne
locale.
# config/locales/hsilgne.yml
hsilgne:
active_interaction:
types:
array: yarra
boolean: naeloob
date: etad
date_time: emit etad
decimal: lamiced
file: elif
float: taolf
hash: hsah
integer: regetni
interface: ecafretni
object: tcejbo
string: gnirts
symbol: lobmys
time: emit
errors:
messages:
invalid: dilavni si
invalid_type: '%{type} dilav a ton si'
missing: deriuqer si
Then set your locale and run interactions like normal.
class I18nInteraction < ActiveInteraction::Base
string :name
end
I18nInteraction.run(name: false).errors.messages[:name]
# => ["is not a valid string"]
I18n.locale = :hsilgne
I18nInteraction.run(name: false).errors.messages[:name]
# => ["gnirts dilav a ton si"]
Everything else works like an activerecord
entry. For example, to rename an attribute you can use attributes
.
Here we'll rename the num
attribute on an interaction named product
:
en:
active_interaction:
attributes:
product:
num: 'Number'
ActiveInteraction is brought to you by Aaron Lasseigne. Along with Aaron, Taylor Fausak helped create and maintain ActiveInteraction but has since moved on.
If you want to contribute to ActiveInteraction, please read our contribution guidelines. A complete list of contributors is available on GitHub.
ActiveInteraction is licensed under the MIT License.
Author: AaronLasseigne
Source code: https://github.com/AaronLasseigne/active_interaction
License: MIT license