Going off the rails
Bundler
- https://quiz.bundlertv.com/
- import data from (30 MB) excel
- quiz collects user taste in TV shows
- algorithm bundles channels and internet packages according to user taste
- checkout for buying over affiliate links
- admin interface

Topic-based organisation
- the default Rails way uses topic-based file structure
- all controllers go in app/controllers
- all models go in app/models
- all topics go in app/topics
- where are domain classes?
- UserForm, OrderCreator, PaymentGatewayClient, CorridorSearchResponse, StatsMarker, NilCompany, CorridorSearchData, CouponsImporter, LastWeekRanks
- application controller lives in app/controllers directory
- isn't _controller.rb unnecessary?
- what is the real reason for suffix?
Component-based organisation
- domain is in app/components
- use module(not class) for component namespace, especially when component spans through multiple files
- use pluralized model name to avoid collision with model class
- user sign up form lives in app/components/userS/form/sign_up.rb
- framework shell remains in default directories, controllers, mailers, models, views...
- related project https://github.com/NullVoxPopuli/drawers
Truncated TDD
- TDDist Red => Green => Refactor
- JIT (feature, feature, ...., feature, specs, commit)
- Kamikaze
What is your distribution of time when coding features?


RSpec.describe Orders::Operation::Create do
context 'success' do
it 'is successful'
it 'creates order'
it 'creates line items'
end
context 'failure' do
it 'is not successful'
it 'contains order errors'
end
end
< 100% test coverage
- code metrics should not drive your development process
- quality over quantity
- but, make sure you write tests that matter
module StarCount # :nodoc:
def self.coverage(score)
case score
when 0..15 then 1
when 15..30 then 2
when 30..51 then 3
when 51..71 then 4
else 5
end
end
end

Decoupling from Rails - Repository
- a simple module encapsulates direct access to model
- pros
- fetching logic is extracted to a single place
- fetching logic can be changed without impact on clients
- cons
- adding simple methods(eg. #find) seems like overkill
- do you test repository?
- other reasons
- direct access to DB(eg. from controller, anywhere)
Decoupling from Rails - Distributing logic
- Business/Domain => Components
- Presentation => Decorators
- Load/Fetch => Repositories
- Persistance => Models
class ContentAddon < ActiveRecord::Base # :nodoc:
def single_channel?
addon_channels.count == 1
end
end
class BundlePackage < ActiveRecord::Base # :nodoc:
def double_play?
package_type == 'double_play' || package_type == 'broadband'
end
end
class Sport < ActiveRecord::Base # :nodoc:
def mark_preincluded(channel)
addon_channels.find_by!(channel: channel)
.update(preincluded: true)
end
def top_show_channel
addon_channels.first.channel
end
end
class Order < ActiveRecord::Base # :nodoc:
def line_items_amount
line_items.selected.map(&:item).sum(&:amount)
end
end
class Profile < ActiveRecord::Base # :nodoc:
validate :favorites_dont_overlap_dislikes
def neutral_buckets
UserBucket.where.not(id: favorite_buckets + disliked_buckets)
end
def subscription_channels
Repository::Subscription.load(subscriptions)
end
private
def favorites_dont_overlap_dislikes
return if favorite_buckets
.none? { |bucket| disliked_buckets.include?(bucket) }
errors.add(
:base, :overlapping_user_buckets,
values: overlapping_user_buckets
)
end
def overlapping_user_buckets
included_in_both(favorite_buckets, disliked_buckets)
.map(&:name).join(', ')
end
def included_in_both(first, second)
first.select { |e| second.include?(e) }
end
end
Algorithm in Rails Ruby
- the algorithm will surely use data from ActiveRecord models
- where do algorithmic methods live? things to consider:
- is solution compliant with SRP?
- excessive DB queries(ensure data is preloaded)
-
not-AR solution => algorithm entity objects
- data (de)serialization logic
- identity(borrowed id from AR model)
- API call
- Preload models needed for calculation and serialize them to entity objects
- Run algorithm
- Deserialize entity objects to models and present to user
Optimise...later

- leave that task to mature
- ready? not yet...
- now? getting close...
- it is easy to optimise understandable well-structured code base(and preferably with green tests)

- complex code benefits from documentation(YARD)
Dependency injection
- Is my class following SRP?
- Am I more complex from manufacturing collaborators?
- Do I require additional dependencies to build collaborator?
- Am I responsible for creating collaborator?
- What is a good place for factory logic?
- client recursive approach:
- client is responsible for constructing collaborating object(s)
- in web application this means controller is responsible to construct all dependencies(transitive also)
- BAD IDEA!
- use (Abstract)Factory pattern
- on large enough scale it comes down to using DI library
- related projects:
I am the last slide!
Questions?
Going off the rails
By vrabac
Going off the rails
- 583