1. Michael Granger
  2. symphony

Commits

Michael Granger  committed 466b8bd

Make time-math stuff in IntervalExpression a little safer.

- Use refinements to avoid polluting core modules
- Use an explicit function instead of #send for interval calulation.
- Fix manifest, add ragel files to the manifest-exclude pattern.

  • Participants
  • Parent commits cddc160
  • Branches default

Comments (0)

Files changed (7)

File .hoerc

View file
  • Ignore whitespace
+--- 
+exclude: !ruby/regexp /tmp$|Gemfile(\.lock)?|Procfile|TAGS|tmtags|\.(hg|bowerrc|keep|hoerc|env|DS_Store|rvm(rc|\.gems)|irbrc|pryrc|tm_.*|rspec.*|rbx|travis\.yml)|(logs|coverage|benchmarks|experiments|features|tmp\w*|wiki|\w+-\d+\.\d+\.\d+)\/|\.(nzb|bundle|sqlite|ru|graffle|espressostorage|esproj|ldif|log|sublime-\w+|pid|pem|gemspec|conf|rl)$|(bower\.json|config\.yml)$/

File Manifest.txt

View file
  • Ignore whitespace
 lib/symphony/task.rb
 lib/symphony/tasks/auditor.rb
 lib/symphony/tasks/failure_logger.rb
-lib/symphony/tasks/pinger.rb
 lib/symphony/tasks/simulator.rb
 lib/symphony/tasks/ssh.rb
 lib/symphony/tasks/sshscript.rb
 spec/helpers.rb
 spec/symphony/daemon_spec.rb
+spec/symphony/intervalexpression_spec.rb
 spec/symphony/mixins_spec.rb
 spec/symphony/queue_spec.rb
 spec/symphony/task_spec.rb

File Rakefile

View file
  • Ignore whitespace
 #!/usr/bin/env rake
 
+require 'pathname'
+
 begin
 	require 'hoe'
 rescue LoadError
 	abort "This Rakefile requires hoe (gem install hoe)"
 end
 
-GEMSPEC = 'symphony.gemspec'
 
-EXPRESSION_RL = 'lib/symphony/intervalexpression.rl'
-EXPRESSION_RB = 'lib/symphony/intervalexpression.rb'
+BASEDIR         = Pathname( __FILE__ ).dirname.relative_path_from( Pathname.pwd )
+
+LIBDIR          = BASEDIR + 'lib'
+SYMPHONY_LIBDIR = LIBDIR + 'symphony'
+
+GEMSPEC         = BASEDIR + 'symphony.gemspec'
+EXPRESSION_RL   = SYMPHONY_LIBDIR + 'intervalexpression.rl'
+EXPRESSION_RB   = SYMPHONY_LIBDIR + 'intervalexpression.rb'
 
 
 Hoe.plugin :mercurial
 
 	spec.require_ruby_version( '>=2.0.0' )
 	spec.hg_sign_tags = true if spec.respond_to?( :hg_sign_tags= )
+	spec.quality_check_whitelist.include( EXPRESSION_RB.to_s ) if
+		spec.respond_to?( :quality_check_whitelist )
 
 	self.rdoc_locations << "deveiate:/usr/local/www/public/code/#{remote_rdoc_dir}"
 end
 # Run the tests before checking in
 task 'hg:precheckin' => [ :check_history, :check_manifest, :spec ]
 
+
 # Rebuild the ChangeLog immediately before release
 task :prerelease => 'ChangeLog'
 CLOBBER.include( 'ChangeLog' )
 
+
 desc "Build a coverage report"
 task :coverage do
 	ENV["COVERAGE"] = 'yes'
 	Rake::Task[:spec].invoke
 end
 
+
+# Generate the expression parser with Ragel
 file EXPRESSION_RL
 file EXPRESSION_RB
 task EXPRESSION_RB => EXPRESSION_RL do |task|
 end
 task :spec => EXPRESSION_RB
 
+
+# Generate a .gemspec file for integration with systems that read it
 task :gemspec => GEMSPEC
 file GEMSPEC => __FILE__ do |task|
 	spec = $hoespec.spec
 		fh.write( spec.to_ruby )
 	end
 end
-
 task :default => :gemspec
 

File lib/symphony/intervalexpression.rl

View file
  • Ignore whitespace
 
 
 require 'symphony' unless defined?( Symphony )
+require 'symphony/mixins'
 require 'time'
 
+using Symphony::TimeRefinements
+
+
 ### Parse natural English expressions of times and intervals.
 ###
 ###  in 30 minutes
 ###  beginning a day from now, run 30 times per minute and finish in 2 weeks
 ###
 class Symphony::IntervalExpression
-	include Comparable
+	include Comparable,
+	        Symphony::TimeFunctions
 	extend Loggability
+
 	log_to :symphony
 
 	# Ragel accessors are injected as class methods/variables for some reason.
 		end
 
 		use_milliseconds = span.sub!( 'milli', '' )
-		interval = duration.to_i.send( span.to_sym )
+		interval = calculate_seconds( duration.to_i, span.to_sym )
 
 		# milliseconds
 		interval = duration.to_f / 1000 if use_milliseconds

File lib/symphony/mixins.rb

View file
  • Ignore whitespace
 	end # module MethodUtilities
 
 
-	### A collection of methods to add to Numeric for convenience (stolen from
-	### ActiveSupport)
-	module NumericConstantMethods
+	# Functions for time calculations
+	module TimeFunctions
 
-		### Append features to the including +mod+.
-		def self::append_features( mod )
-			constants.each do |c|
-				self.const_get( c ).send( :append_features, mod )
-			end
-			super
+		###############
+		module_function
+		###############
+
+		### Calculate the (approximate) number of seconds that are in +count+ of the
+		### given +unit+ of time.
+		def calculate_seconds( count, unit )
+			return case unit
+				when :seconds, :second
+					count
+				when :minutes, :minute
+					count * 60
+				when :hours, :hour
+					count * 3600
+				when :days, :day
+					count * 86400
+				when :weeks, :week
+					count * 604800
+				when :fortnights, :fortnight
+					count * 1209600
+				when :months, :month
+					count * 2592000
+				when :years, :year
+					count * 31557600
+				else
+					raise ArgumentError, "don't know how to calculate seconds in a %p" % [ unit ]
+				end
 		end
 
-		### Time constants
-		module Time
+	end # module TimeFunctions
+
+
+	# Refinements to Numeric to add time-related convenience methods
+	module TimeRefinements
+		refine Numeric do
 
 			### Number of seconds (returns receiver unmodified)
 			def seconds
 
 			### Returns number of seconds in <receiver> minutes
 			def minutes
-				return self * 60
+				return TimeFunctions.calculate_seconds( self, :minutes )
 			end
 			alias_method :minute, :minutes
 
 			### Returns the number of seconds in <receiver> hours
 			def hours
-				return self * 60.minutes
+				return TimeFunctions.calculate_seconds( self, :hours )
 			end
 			alias_method :hour, :hours
 
 			### Returns the number of seconds in <receiver> days
 			def days
-				return self * 24.hours
+				return TimeFunctions.calculate_seconds( self, :day )
 			end
 			alias_method :day, :days
 
 			### Return the number of seconds in <receiver> weeks
 			def weeks
-				return self * 7.days
+				return TimeFunctions.calculate_seconds( self, :weeks )
 			end
 			alias_method :week, :weeks
 
 			### Returns the number of seconds in <receiver> fortnights
 			def fortnights
-				return self * 2.weeks
+				return TimeFunctions.calculate_seconds( self, :fortnights )
 			end
 			alias_method :fortnight, :fortnights
 
 			### Returns the number of seconds in <receiver> months (approximate)
 			def months
-				return self * 30.days
+				return TimeFunctions.calculate_seconds( self, :months )
 			end
 			alias_method :month, :months
 
 			### Returns the number of seconds in <receiver> years (approximate)
 			def years
-				return (self * 365.25.days).to_i
+				return TimeFunctions.calculate_seconds( self, :years )
 			end
 			alias_method :year, :years
 
 
-			### Returns the Time <receiver> number of seconds before the 
+			### Returns the Time <receiver> number of seconds before the
 			### specified +time+. E.g., 2.hours.before( header.expiration )
 			def before( time )
 				return time - self
 			end
 
 
-			### Returns the Time <receiver> number of seconds ago. (e.g., 
+			### Returns the Time <receiver> number of seconds ago. (e.g.,
 			### expiration > 2.hours.ago )
 			def ago
 				return self.before( ::Time.now )
 			end
 
 
-			### Return a string describing approximately the amount of time in 
+			### Return a string describing approximately the amount of time in
 			### <receiver> number of seconds.
 			def timeperiod
 				return case
 					end
 			end
 
-		end # module Time
-	end
+		end # refine Numeric
+	end # module TimeRefinements
 
 end # module Symphony
 
 
-### Add convenience methods to Numerics
-class Numeric
-	include Symphony::NumericConstantMethods
-end
-

File spec/symphony/intervalexpression_spec.rb

View file
  • Ignore whitespace
 
 require_relative '../helpers'
 require 'symphony/intervalexpression'
+require 'symphony/mixins'
+
+using Symphony::TimeRefinements
+
 
 #####################################################################
 ###	C O N T E X T S

File spec/symphony/mixins_spec.rb

View file
  • Ignore whitespace
 
 require 'symphony/mixins'
 
+using Symphony::TimeRefinements
 
 describe Symphony, 'mixins' do