Skip to content

Commit

Permalink
Step #1: Initial setup
Browse files Browse the repository at this point in the history
 - Complex unrefactored Process object
 - Basic test that does 100% coverage
 - dependencies file that stubs external dependencies
 - Gemfile
  • Loading branch information
Bohdan Pohorilets committed Apr 20, 2018
1 parent ab057ad commit a8bf624
Show file tree
Hide file tree
Showing 5 changed files with 345 additions and 0 deletions.
6 changes: 6 additions & 0 deletions samples/two_level_deep_service_objects/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
source 'https://rubygems.org'
gem 'rspec'
gem 'rspec-mocks'
gem 'activesupport'
gem 'activemodel'
gem 'simplecov'
43 changes: 43 additions & 0 deletions samples/two_level_deep_service_objects/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
GEM
remote: https://rubygems.org/
specs:
activemodel (5.1.5)
activesupport (= 5.1.5)
activesupport (5.1.5)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (~> 0.7)
minitest (~> 5.1)
tzinfo (~> 1.1)
concurrent-ruby (1.0.5)
diff-lcs (1.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
minitest (5.11.3)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.1)
thread_safe (0.3.6)
tzinfo (1.2.5)
thread_safe (~> 0.1)

PLATFORMS
ruby

DEPENDENCIES
activemodel
activesupport
rspec
rspec-mocks

BUNDLED WITH
1.16.1
124 changes: 124 additions & 0 deletions samples/two_level_deep_service_objects/dependencies.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
class StaffActionLogger
def initialize(*)
true
end

def log_name_change(*)
true
end
end

class User
include ::ActiveModel::Model
attr_accessor :date_of_birth
attr_accessor :name
attr_accessor :user_profile
attr_accessor :locale
attr_accessor :title
attr_accessor :user_option
attr_accessor :custom_fields

def id
1
end

def reload
self
end

def save
true
end

def custom_fields
{}
end

def self.transaction
yield
end
end

class UserProfile
attr_accessor :card_background
attr_accessor :location
attr_accessor :profile_background
attr_accessor :website
attr_accessor :bio_raw

def save
true
end

def website
'http://www.example.com'
end
end

class Guardian
attr_accessor :user
private

def initialize(user)
@user = user
end
end

class SiteSetting
cattr_accessor :sso_url
cattr_writer :enable_sso
cattr_writer :sso_overrides_bio

def self.enable_sso
@enable_sso || true
end

def self.sso_overrides_bio
@sso_overrides_bio || false
end
end

class UserUpdater
CATEGORY_IDS = {
watched_first_post_category_ids: :watching_first_post,
watched_category_ids: :watching,
tracked_category_ids: :tracking,
muted_category_ids: :muted
}

TAG_NAMES = {
watching_first_post_tags: :watching_first_post,
watched_tags: :watching,
tracked_tags: :tracking,
muted_tags: :muted
}

OPTION_ATTR = [
:email_always,
:mailing_list_mode,
:mailing_list_mode_frequency,
:email_digests,
:email_direct,
:email_private_messages,
:external_links_in_new_tab,
:enable_quoting,
:dynamic_favicon,
:disable_jump_reply,
:automatically_unpin_topics,
:digest_after_minutes,
:new_topic_duration_minutes,
:auto_track_topics_after_msecs,
:notification_level_when_replying,
:email_previous_replies,
:email_in_reply_to,
:like_notification_frequency,
:include_tl0_in_digests,
:theme_key,
:allow_private_messages,
:homepage_id,
]

def can_grant_title?(user)
true
end
end
88 changes: 88 additions & 0 deletions samples/two_level_deep_service_objects/user_updater.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
require 'active_support'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/time/calculations'
require 'active_model'

require_relative 'dependencies'

class UserUpdater
delegate :change_post_owner, to: :guardian

def update(attributes = {})
save_options = false
saved = nil
old_user_name = user.name.present? ? user.name : ""

user_profile = user.user_profile
user_profile.location = attributes.fetch(:location) { user_profile.location }
user_profile.dismissed_banner_key = attributes[:dismissed_banner_key] if attributes[:dismissed_banner_key].present?
user_profile.website = format_url(attributes.fetch(:website) { user_profile.website })
user_profile.profile_background = attributes.fetch(:profile_background) { user_profile.profile_background }
user_profile.card_background = attributes.fetch(:card_background) { user_profile.card_background }
if SiteSetting.enable_sso && !SiteSetting.sso_overrides_bio
user_profile.bio_raw = attributes.fetch(:bio_raw) { user_profile.bio_raw }
end

user.name = attributes.fetch(:name) { user.name }
user.locale = attributes.fetch(:locale) { user.locale }
user.date_of_birth = attributes.fetch(:date_of_birth) { user.date_of_birth }
if can_grant_title?(user)
user.title = attributes.fetch(:title) { user.title }
end

# special handling for theme_key cause we need to bump a sequence number
if attributes.key?(:theme_key) && user.user_option.theme_key != attributes[:theme_key]
user.user_option.theme_key_seq += 1
end

OPTION_ATTR.each do |attribute|
if attributes.key?(attribute)
save_options = true

if [true, false].include?(user.user_option.send(attribute))
val = attributes[attribute].to_s == 'true'
user.user_option.send("#{attribute}=", val)
else
user.user_option.send("#{attribute}=", attributes[attribute])
end
end
end

# automatically disable digests when mailing_list_mode is enabled
user.user_option.email_digests = false if user.user_option.mailing_list_mode

fields = attributes[:custom_fields]
if fields.present?
user.custom_fields = user.custom_fields.merge(fields)
end

User.transaction do
if user.user_option.save && user_profile.save && user.save
StaffActionLogger.new(@actor).log_name_change(
user.id,
old_user_name,
attributes.fetch(:name) { '' }
)

saved = true
end
end

saved
end

private

attr_reader :user, :guardian

def initialize(actor, user, guardian = Guardian.new(actor))
@user = user
@guardian = guardian
@actor = actor
end

def format_url(website)
return nil if website.blank?
website =~ /^http/ ? website : "http://#{website}"
end
end
84 changes: 84 additions & 0 deletions samples/two_level_deep_service_objects/user_updater_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
require 'simplecov'
SimpleCov.start

require_relative 'user_updater'

describe UserUpdater do
let( :acting_user ) { User.new( name: 'Acting User', user_profile: UserProfile.new ) }

describe '#update' do
it 'saves user' do
user = User.new( name: 'Billy Bob', user_profile: UserProfile.new, user_option: spy( email_always: true ) )

updater = UserUpdater.new( acting_user, user )
updater.update( name: 'Jim Tom' )

expect( user.reload.name ).to eq 'Jim Tom'
end

it 'updates various fields' do
user = User.new( name: 'Billy Bob', user_profile: UserProfile.new, user_option: spy( email_always: true ) )
updater = UserUpdater.new(acting_user, user)
date_of_birth = Time.current

val = updater.update(bio_raw: 'my new bio',
email_always: 'true',
mailing_list_mode: true,
digest_after_minutes: "45",
new_topic_duration_minutes: 100,
auto_track_topics_after_msecs: 101,
notification_level_when_replying: 3,
email_in_reply_to: false,
date_of_birth: date_of_birth,
theme_key: 'theme.key',
custom_fields: { one: :two },
allow_private_messages: false)

expect(val).to be_truthy

user.reload

expect(user.user_profile.bio_raw).to eq 'my new bio'
end

it "disables email_digests when enabling mailing_list_mode" do
user = User.new( name: 'Billy Bob', user_profile: UserProfile.new, user_option: spy( email_always: true ) )
updater = UserUpdater.new(acting_user, user)

val = updater.update(mailing_list_mode: true, email_digests: true)
expect(val).to be_truthy

user.reload

expect(user.user_option).to have_received(:mailing_list_mode=).
with( true )
end

context 'when sso overrides bio' do
it 'changes bio' do
SiteSetting.sso_url = "https://www.example.com/sso"
SiteSetting.enable_sso = true
SiteSetting.sso_overrides_bio = true

user = User.new( name: 'Billy Bob', user_profile: UserProfile.new, user_option: spy( email_always: true ) )
updater = UserUpdater.new(acting_user, user)

expect(updater.update(bio_raw: "new bio")).to be_truthy

user.reload
expect(user.user_profile.bio_raw).to eq 'new bio'
end
end

context 'when update fails' do
it 'returns false' do
user = User.new( name: 'Billy Bob', user_profile: UserProfile.new, user_option: spy( email_always: true ) )
allow(user).to receive( :save ).
and_return( false )
updater = UserUpdater.new(acting_user, user)

expect(updater.update).to be_falsey
end
end
end
end

0 comments on commit a8bf624

Please sign in to comment.