SlideShare une entreprise Scribd logo
1  sur  42
Télécharger pour lire hors ligne
Objectify Your Forms:
Beyond Basic User Input
DannyOlson
dbolson@gmail.com
Corporate Sponsorship
Sharethrough
In-feed advertisingexchange.
Background
Forms are common in web applications
Forms often end up savingdatato multiple tables
Rails gives us #accepts_nested_attributes_for
Much magic and potentialconfusion
tl;dr
We don'talways have to do things The Rails Way™
Ice Cream
Memes
Online Meme-Based Ice Cream
Ordering Service
Class Diagram
First Implementation
The Rails Way™
classIceCreamsController<ApplicationController
defnew
@ice_cream=IceCream.new
respond_with@ice_cream
end
defcreate
@ice_cream=IceCream.new(valid_params)
if@ice_cream.save
Meme.create_defaults(@ice_cream)
redirect_toedit_ice_cream_path(@ice_cream),
notice:'Icecreamwascreated.'
else
render:new
end
end
=form_forice_creamdo|f|
-#otherfieldsomitted
-ifice_cream.persisted?
=f.fields_for:memesdo|fields|
=fields.label:name
=fields.text_field:name
=fields.label:rating
=fields.select:rating,
options_for_select(10.downto(1),fields.object.rating)
-iffields.object.persisted?
=fields.label:_destroy,'Delete'
=fields.check_box:_destroy
=f.submit'MakeItSo(Delicious)'
classIceCream<ActiveRecord::Base
accepts_nested_attributes_for:memes,
reject_if:proc{|attr|
attr['name'].blank?||attr['rating'].blank?
},
allow_destroy:true
validates:flavor_id,:serving_size_id,presence:true
validates:scoops,presence:true,inclusion:{in:[1,2,3]}
validate:more_scoops_than_toppings
before_save:set_price
deftopping_ids=(toppings)
filtered_toppings=toppings.reject{|t|!Topping.exists?(t)}
super(filtered_toppings)
end
private
defmore_scoops_than_toppings
ifscoops.to_i<toppings.size
errors.add(:toppings,"can'tbemorethanscoops")
end
end
defset_price
self.price=scoops*100
end
Responsibilities
classIceCream<ActiveRecord::Base
reject_if:proc{|attr|
attr['name'].blank?||attr['rating'].blank?
},
allow_destroy:true
Responsibilities
classIceCream<ActiveRecord::Base
before_save:set_price
private
defset_price
self.price=scoops*100
end
Responsibilities
classIceCream<ActiveRecord::Base
deftopping_ids=(toppings)
filtered_toppings=toppings.reject{|t|!Topping.exists?(t)}
super(filtered_toppings)
end
Responsibilities
classIceCream<ActiveRecord::Base
validates:flavor_id,presence:true
validates:serving_size_id,presence:true
validates:scoops,presence:true,inclusion:{in:[1,2,3]}
validate:more_scoops_than_toppings
private
defmore_scoops_than_toppings
ifscoops.to_i<toppings.size
errors.add(:toppings,"can'tbemorethanscoops")
end
end
Single Responsibility Principle
Every class should have one, and only one,
reason to change.
1. formatdata
2. save the data
3. check values of associated objects
4. validations
Concerning...
Early on, SRPis easy to apply. ActiveRecord
classes handle persistence, associations and not
much else. Butbit-by-bit, they grow. Objects
thatare inherently responsible for persistence
become the de facto owner of all business logic
as well. And ayear or two later you have aUser
class with over 500 lines of code, and hundreds
of methods in it’s public interface.
- 7 Patterns to Refactor FatActiveRecord
Models
#accepts_nested_attributes_foris
used, in ActiveRecord classes, to reduce the
amountof code in Rails applications needed to
create/update records across multiple tables
with asingle HTTPPOST/PUT. As with many
things Rails, this is convention-driven...
While this sometimes results in less code, itoften
results in brittle code.
- #accepts_nested_attributes_for (Often)
Considered Harmful
New Feature Request
We need to base the price off of both
the memes and the amountof scoops.
From this:
#app/models/ice_cream.rb
classIceCream<ActiveRecord::Base
defset_price
self.price=scoops*100
end
#app/controllers/ice_creams_controller.rb
classIceCreamsController<ApplicationController
defcreate
@ice_cream=IceCream.new(valid_params)
if@ice_cream.save
Meme.create_defaults(@ice_cream)
redirect_toedit_ice_cream_path(@ice_cream),
notice:'Icecreamwascreated.'
else
render:new
end
end
To this:
#app/models/ice_cream.rb
classIceCream<ActiveRecord::Base
defratings_sum
memes.reduce(0){|sum,meme|sum+=meme.rating}
end
defset_price
unlessprice_changed?
self.price=scoops*100
end
end
#app/controllers/ice_creams_controller.rb
classIceCreamsController<ApplicationController
defcreate
@ice_cream=IceCream.new(valid_params)
if@ice_cream.save
Meme.create_defaults(@ice_cream)
meme_ratings=@ice_cream.ratings_sum
@ice_cream.update_attributes!({
price:@ice_cream.price+meme_ratings
})
redirect_toedit_ice_cream_path(@ice_cream),
notice:'Icecreamwascreated.'
else
render:new
end
end
There's Another Way
Form Object
An objectthatencapsulates context-specific
logic for user input.
Ithas onlythe attributes displayed in the form
Itsets up its own data
Itvalidates thatdata
Itdelegates to persistence butdoesn'tknow specifics
Second Implementation
With a Form Object
classNewOrderForm
includeVirtus.model
attribute:flavor_id,Integer
attribute:serving_size_id,Integer
attribute:scoops,Integer
attribute:topping_ids,Array[Integer]
end
classNewOrderForm
extendActiveModel::Naming
includeActiveModel::Conversion
includeActiveModel::Validations
validates:flavor_id,:serving_size_id,presence:true
validates:scoops,presence:true,inclusion:{in:[1,2,3]}
validate:more_scoops_than_toppings
private
defmore_scoops_than_toppings
ifscoops.to_i<topping_ids.delete_if{|attr|attr==''}.size
errors.add(:topping_ids,"can'tbemorethanscoops")
end
end
classNewOrderForm
defsave
ifvalid?
@model=OrderCreating.call(attributes)
true
else
false
end
end
classOrderCreating
defself.call(attributes)
IceCream.transactiondo
ice_cream=IceCream.create!(flavor_id:flavor_id,
serving_size_id:serving_size_id,
topping_ids:topping_ids,
scoops:scoops,
price:scoops*100)
Meme.create_defaults(ice_cream)
ice_cream
end
end
end
classEditOrderForm
attribute:memes,Array[EditMemeForm]
classEditMeme
attribute:id,Integer
attribute:name,String
attribute:rating,Integer
attribute:_destroy,Boolean,default:false
validates:name,presence:true
validates:rating,
presence:true,
inclusion:{in:1..10,message:'mustbebetween1and10'}
end
classOrdersController<ApplicationController
defnew
@order=NewOrderForm.new
respond_with@order
end
defcreate
@order=NewOrderForm.new(valid_params)
if@order.save
redirect_toedit_order_path(@order),
notice:'Yourorderwascreated.'
else
render:new
end
end
end
-iforder.persisted?
-order.memes.each_with_indexdo|meme,index|
-ifmeme.id
=hidden_field_tag"order[memes][][id]",meme.id
=label_tag"order[memes][][name]",'Name'
=text_field_tag"order[memes][][name]",meme.name
=label_tag"order[memes][][rating]",'Rating'
=select_tag"order[memes][][rating]",meme_rating_options
-ifmeme.id
=label_tag"memes_destroy_#{index}"do
=check_box_tag"order[memes][][_destroy]"
Delete
classIceCream<ActiveRecord::Base
belongs_to:flavor
belongs_to:serving_size
has_and_belongs_to_many:toppings
has_many:memes,dependent::destroy
end
New Feature Request Redux
We need to base the price off of the meme ratings
notjustthe amountof scoops.
From this:
classOrderCreating
defself.call(attributes)
IceCream.transactiondo
ice_cream=IceCream.create!(flavor_id:flavor_id,
serving_size_id:serving_size_id,
topping_ids:topping_ids,
scoops:scoops,
price:scoops*100)
Meme.create_defaults(ice_cream)
ice_cream
end
end
end
To this:
classOrderCreating
defself.call(attributes)
IceCream.transactiondo
ice_cream=IceCream.create!(flavor_id:flavor_id,
serving_size_id:serving_size_id,
topping_ids:topping_ids,
scoops:scoops,
price:scoops*100)
Meme.create_defaults(ice_cream)
IceCreamPriceUpdating.call(ice_cream)
end
end
end
classIceCreamPriceUpdating
defself.call
meme_ratings=ice_cream.memes.reduce(0){|sum,meme|
sum+=meme.rating
}
ice_cream.update_attributes!(price:ice_cream.price+meme_ratings)
ice_cream
end
Benefits
Clearer domain model
Less magic
Simpler to test
Onlyneed to consider the specific context
Easier to maintain and change
When to Use a Form Object
A Rule (if you want one):
Use a form object when persisting
multiple ActiveRecord models
What's Out There?
redtape gem
activeform-rails gem
Thank You
Links
dbolson@gmail.com
https://github.com/dbolson/form-object-presentation
https://github.com/solnic/virtus
http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-
activerecord-models
http://evan.tiggerpalace.com/articles/2012/11/07/accepts_nested_attributes_for-
often-considered-harmful/
https://github.com/ClearFit/redtape
https://github.com/GCorbel/activeform-rails

Contenu connexe

Similaire à Objectify Your Forms: Beyond Basic User Input

DrupalCamp LA 2012: COOK UP SOME STACKS OF DRUPAL GOODNESS WITH CHEF
DrupalCamp LA 2012: COOK UP SOME STACKS OF DRUPAL GOODNESS WITH CHEFDrupalCamp LA 2012: COOK UP SOME STACKS OF DRUPAL GOODNESS WITH CHEF
DrupalCamp LA 2012: COOK UP SOME STACKS OF DRUPAL GOODNESS WITH CHEFccmcnerdy
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin GeneratorJohn Cleveley
 
Optimizing and Extending Xamarin.Forms iOS, Android, and UWP Apps
Optimizing and Extending Xamarin.Forms iOS, Android, and UWP AppsOptimizing and Extending Xamarin.Forms iOS, Android, and UWP Apps
Optimizing and Extending Xamarin.Forms iOS, Android, and UWP AppsJames Montemagno
 
Apex Enterprise Patterns: Building Strong Foundations
Apex Enterprise Patterns: Building Strong FoundationsApex Enterprise Patterns: Building Strong Foundations
Apex Enterprise Patterns: Building Strong FoundationsSalesforce Developers
 
War of the Indices- SQL Server and Oracle
War of the Indices-  SQL Server and OracleWar of the Indices-  SQL Server and Oracle
War of the Indices- SQL Server and OracleKellyn Pot'Vin-Gorman
 
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015Fernando Hamasaki de Amorim
 
Sugar U: Session 7: An Introduction to Sugar Development, Going Way Beyond St...
Sugar U: Session 7: An Introduction to Sugar Development, Going Way Beyond St...Sugar U: Session 7: An Introduction to Sugar Development, Going Way Beyond St...
Sugar U: Session 7: An Introduction to Sugar Development, Going Way Beyond St...SugarCRM
 
extending-and-optimizing-xamarin-forms-apps
extending-and-optimizing-xamarin-forms-appsextending-and-optimizing-xamarin-forms-apps
extending-and-optimizing-xamarin-forms-appsMatthew Soucoup
 
Masterclass Webinar: Amazon DynamoDB July 2014
Masterclass Webinar: Amazon DynamoDB July 2014Masterclass Webinar: Amazon DynamoDB July 2014
Masterclass Webinar: Amazon DynamoDB July 2014Amazon Web Services
 
Os piores códigos Ruby já vistos - TDC Florianópolis 2016
Os piores códigos Ruby já vistos - TDC Florianópolis 2016Os piores códigos Ruby já vistos - TDC Florianópolis 2016
Os piores códigos Ruby já vistos - TDC Florianópolis 2016Fernando Hamasaki de Amorim
 
Migrate yourself. code -> module -> mind
Migrate yourself. code -> module -> mindMigrate yourself. code -> module -> mind
Migrate yourself. code -> module -> mindValentine Matsveiko
 
Валентин Мацвейко та Владислав Мойсеєнко — D8: Migrate Yourself: code->module...
Валентин Мацвейко та Владислав Мойсеєнко — D8: Migrate Yourself: code->module...Валентин Мацвейко та Владислав Мойсеєнко — D8: Migrate Yourself: code->module...
Валентин Мацвейко та Владислав Мойсеєнко — D8: Migrate Yourself: code->module...LEDC 2016
 
Evolutionary db development
Evolutionary db development Evolutionary db development
Evolutionary db development Open Party
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on RailsMark Menard
 
Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy Codescidept
 
An approach to implement model classes in zend
An approach to implement model classes in zendAn approach to implement model classes in zend
An approach to implement model classes in zendswiss IT bridge
 
AngularJS: What's the Big Deal?
AngularJS: What's the Big Deal?AngularJS: What's the Big Deal?
AngularJS: What's the Big Deal?Jim Duffy
 
Drupal tips 'n tricks
Drupal tips 'n tricksDrupal tips 'n tricks
Drupal tips 'n tricksJohn Tsevdos
 

Similaire à Objectify Your Forms: Beyond Basic User Input (20)

DrupalCamp LA 2012: COOK UP SOME STACKS OF DRUPAL GOODNESS WITH CHEF
DrupalCamp LA 2012: COOK UP SOME STACKS OF DRUPAL GOODNESS WITH CHEFDrupalCamp LA 2012: COOK UP SOME STACKS OF DRUPAL GOODNESS WITH CHEF
DrupalCamp LA 2012: COOK UP SOME STACKS OF DRUPAL GOODNESS WITH CHEF
 
Working With The Symfony Admin Generator
Working With The Symfony Admin GeneratorWorking With The Symfony Admin Generator
Working With The Symfony Admin Generator
 
Optimizing and Extending Xamarin.Forms iOS, Android, and UWP Apps
Optimizing and Extending Xamarin.Forms iOS, Android, and UWP AppsOptimizing and Extending Xamarin.Forms iOS, Android, and UWP Apps
Optimizing and Extending Xamarin.Forms iOS, Android, and UWP Apps
 
Apex Enterprise Patterns: Building Strong Foundations
Apex Enterprise Patterns: Building Strong FoundationsApex Enterprise Patterns: Building Strong Foundations
Apex Enterprise Patterns: Building Strong Foundations
 
War of the Indices- SQL Server and Oracle
War of the Indices-  SQL Server and OracleWar of the Indices-  SQL Server and Oracle
War of the Indices- SQL Server and Oracle
 
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
 
Sugar U: Session 7: An Introduction to Sugar Development, Going Way Beyond St...
Sugar U: Session 7: An Introduction to Sugar Development, Going Way Beyond St...Sugar U: Session 7: An Introduction to Sugar Development, Going Way Beyond St...
Sugar U: Session 7: An Introduction to Sugar Development, Going Way Beyond St...
 
The Rails Way
The Rails WayThe Rails Way
The Rails Way
 
extending-and-optimizing-xamarin-forms-apps
extending-and-optimizing-xamarin-forms-appsextending-and-optimizing-xamarin-forms-apps
extending-and-optimizing-xamarin-forms-apps
 
Masterclass Webinar: Amazon DynamoDB July 2014
Masterclass Webinar: Amazon DynamoDB July 2014Masterclass Webinar: Amazon DynamoDB July 2014
Masterclass Webinar: Amazon DynamoDB July 2014
 
Os piores códigos Ruby já vistos - TDC Florianópolis 2016
Os piores códigos Ruby já vistos - TDC Florianópolis 2016Os piores códigos Ruby já vistos - TDC Florianópolis 2016
Os piores códigos Ruby já vistos - TDC Florianópolis 2016
 
Migrate yourself. code -> module -> mind
Migrate yourself. code -> module -> mindMigrate yourself. code -> module -> mind
Migrate yourself. code -> module -> mind
 
Валентин Мацвейко та Владислав Мойсеєнко — D8: Migrate Yourself: code->module...
Валентин Мацвейко та Владислав Мойсеєнко — D8: Migrate Yourself: code->module...Валентин Мацвейко та Владислав Мойсеєнко — D8: Migrate Yourself: code->module...
Валентин Мацвейко та Владислав Мойсеєнко — D8: Migrate Yourself: code->module...
 
Evolutionary db development
Evolutionary db development Evolutionary db development
Evolutionary db development
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on Rails
 
Yii Introduction
Yii IntroductionYii Introduction
Yii Introduction
 
Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy Code
 
An approach to implement model classes in zend
An approach to implement model classes in zendAn approach to implement model classes in zend
An approach to implement model classes in zend
 
AngularJS: What's the Big Deal?
AngularJS: What's the Big Deal?AngularJS: What's the Big Deal?
AngularJS: What's the Big Deal?
 
Drupal tips 'n tricks
Drupal tips 'n tricksDrupal tips 'n tricks
Drupal tips 'n tricks
 

Dernier

Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherRemote DBA Services
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDropbox
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century educationjfdjdjcjdnsjd
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobeapidays
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbuapidays
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsNanddeep Nachan
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProduct Anonymous
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfsudhanshuwaghmare1
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...apidays
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024The Digital Insurer
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...Zilliz
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesrafiqahmad00786416
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxRustici Software
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CVKhem
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdflior mazor
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MIND CTI
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 

Dernier (20)

Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
ICT role in 21st century education and its challenges
ICT role in 21st century education and its challengesICT role in 21st century education and its challenges
ICT role in 21st century education and its challenges
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Real Time Object Detection Using Open CV
Real Time Object Detection Using Open CVReal Time Object Detection Using Open CV
Real Time Object Detection Using Open CV
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 

Objectify Your Forms: Beyond Basic User Input