Changeset 287

Show
Ignore:
Timestamp:
09/30/08 08:05:39 (7 weeks ago)
Author:
ged
Message:

Checkpoint commit

Location:
trunk
Files:
5 added
14 modified

Legend:

Unmodified
Added
Removed
  • trunk/LICENSE

    r285 r287  
     1Copyright (c) 2008, Michael Granger 
     2All rights reserved. 
    13 
    2 Copyright (c) 2000-2008 The FaerieMUD Consortium. All rights reserved. 
     4Redistribution and use in source and binary forms, with or without 
     5modification, are permitted provided that the following conditions are met: 
    36 
    4 While the server this code runs on (MUES) is distributed under an Open Source license, the code for 
    5 the FaerieMUD game world is not (currently) Open Source or Free Software. In order to preserve what 
    6 we anticipate will be a unique game world, and to keep some aspects of the game semi-obscured, we 
    7 would rather keep the code from being widely distributed for the time being. It is entirely possible 
    8 that we may choose to distribute it under a Free Software or Open Source license in the future. 
     7  * Redistributions of source code must retain the above copyright notice, 
     8    this list of conditions and the following disclaimer. 
    99 
    10 THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT 
    11 LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
     10  * Redistributions in binary form must reproduce the above copyright notice, 
     11    this list of conditions and the following disclaimer in the documentation 
     12    and/or other materials provided with the distribution. 
    1213 
     14  * Neither the name of the author/s, nor the names of the project's 
     15    contributors may be used to endorse or promote products derived from this 
     16    software without specific prior written permission. 
     17 
     18THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
     19AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
     20IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
     21DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 
     22FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
     23DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
     24SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
     25CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
     26OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
     27OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  • trunk/Rakefile

    r285 r287  
    199199    end 
    200200     
    201     DEVELOPMENT_DEPENDENCIES.each do |name, version| 
    202         version = '>= 0' if version.length.zero? 
    203         gem.add_development_dependency( name, version ) 
     201    # Developmental dependencies don't work as of RubyGems 1.2.0 
     202    unless Gem::Version.new( Gem::RubyGemsVersion ) <= Gem::Version.new( "1.2.0" ) 
     203        DEVELOPMENT_DEPENDENCIES.each do |name, version| 
     204            version = '>= 0' if version.length.zero? 
     205            gem.add_development_dependency( name, version ) 
     206        end 
    204207    end 
    205208     
  • trunk/lib/fm/composedobject.rb

    r285 r287  
    5959### parts which together define the behaviour of the whole. 
    6060class FaerieMUD::ComposedObject < FaerieMUD::Entity 
     61    include FaerieMUD::AbstractClass 
     62     
    6163    contributors :ged, :scotus 
    6264 
     
    6567    module Constituent 
    6668 
     69        ### Methods which are added to including classes as class methods. 
     70        module ClassMethods 
     71            # A two-element Array containing the names of the aspects of 
     72            # the statistic that belongs to the Constituent -- 
     73            # developmental and linear. 
     74            attr_reader :statistic_names 
     75 
     76            ### Create accessors for the constituent's 
     77            ### FaerieMUD::Statistic object with the given +devel_name+, 
     78            ### which will return the Statistic's developmental aspect, 
     79            ### and +linear_name+, which will return the Statistic's 
     80            ### linear constituent. 
     81            def def_statistic( devel_name, linear_name ) 
     82                devel = devel_name.to_s.intern 
     83                linear = linear_name.to_s.intern 
     84 
     85                self.statistic_names.replace([ devel_name, linear_name ]) 
     86 
     87                define_method( devel ) { self.statistic.developmental } 
     88                define_method( "#{devel}=" ) {|arg| 
     89                    check_type( arg, FaerieMUD::DevelopmentalObject ) 
     90                    self.statistic.developmental = arg 
     91                } 
     92 
     93                define_method( linear ) { self.statistic.linear } 
     94                define_method( "#{linear}=" ) {|arg| 
     95                    self.statistic.linear = arg 
     96                } 
     97            end 
     98        end 
     99 
     100 
    67101        ### Inclusion callback -- Install a 'def_statistic' function in 
    68102        ### including classes to auto-generate statistic accessors. 
    69103        def self::included( klass ) 
    70             klass.instance_variable_set( :@statisticNames, [] ) 
    71  
    72             class << klass 
    73  
    74                 # A two-element Array containing the names of the aspects of 
    75                 # the statistic that belongs to the Constituent -- 
    76                 # developmental and linear. 
    77                 attr_reader :statisticNames 
    78  
    79                 ### Create accessors for the constituent's 
    80                 ### FaerieMUD::Statistic object with the given +devel_name+, 
    81                 ### which will return the Statistic's developmental aspect, 
    82                 ### and +linear_name+, which will return the Statistic's 
    83                 ### linear constituent. 
    84                 def def_statistic( devel_name, linear_name ) 
    85                     devel = devel_name.to_s.intern 
    86                     linear = linear_name.to_s.intern 
    87  
    88                     self.statisticNames.replace([ devel_name, linear_name ]) 
    89  
    90                     define_method( devel ) { self.statistic.developmental } 
    91                     define_method( "#{devel}=" ) {|arg| 
    92                         check_type( arg, FaerieMUD::DevelopmentalObject ) 
    93                         self.statistic.developmental = arg 
    94                     } 
    95  
    96                     define_method( linear ) { self.statistic.linear } 
    97                     define_method( "#{linear}=" ) {|arg| 
    98                         self.statistic.linear = arg 
    99                     } 
    100                 end 
    101             end 
    102         end 
    103  
     104            klass.instance_variable_set( :@statistic_names, [] ) 
     105            klass.extend( ClassMethods ) 
     106        end 
     107 
     108 
     109        ############################################################# 
     110        ### I N S T A N C E   M E T H O D S 
     111        ############################################################# 
    104112 
    105113        ### Initialize the "statistic" part of a Constituent object. It uses 
     
    107115        def initialize( args={} ) 
    108116            raise RuntimeError, "No statistic defined for #{self.class.inspect}" if 
    109                 self.class.statisticNames.empty? 
     117                self.class.statistic_names.empty? 
    110118 
    111119            # If the statistic is defined in the args, remove it from the 
     
    114122            element = args.delete( :statistic ) || 1 
    115123            @statistic = FaerieMUD::Statistic.new( 
    116                 :devel_name => self.class.statisticNames[0], 
    117                 :linear_name => self.class.statisticNames[1], 
    118                 :element => element ) 
     124                self.class.statistic_names[0], 
     125                self.class.statistic_names[1], 
     126                element ) 
    119127 
    120128            super 
     
    156164    ### break. 
    157165    class Bifurcation < FaerieMUD::Entity 
     166        include FaerieMUD::AbstractClass 
     167         
    158168        contributors :ged 
    159169 
     
    187197 
    188198        ### Picker method: should return the "current" constituent. 
    189         abstract :current 
     199        virtual :current 
    190200 
    191201 
     
    296306            # Make sure none of the aspect-names for the constituents' 
    297307            # statistics collide with each other. 
    298             develAspect, linearAspect = klass.statisticNames 
     308            develAspect, linearAspect = klass.statistic_names 
    299309            if statAspects.key?( develAspect ) 
    300310                raise FaerieMUD::ConstituentError, 
     
    335345            # Gather symbols for the methods we'll be creating 
    336346            nameEq = "#{name}=" 
    337             develAspect, linearAspect = klass.statisticNames 
     347            develAspect, linearAspect = klass.statistic_names 
    338348            develAspectEq = "#{develAspect}=".intern 
    339349            linearAspectEq = "#{linearAspect}=".intern 
  • trunk/lib/fm/gameobject.rb

    r285 r287  
    33require 'digest/md5' 
    44require 'logger' 
    5 require 'monitor' 
     5require 'sync' 
     6require 'uuidtools' 
    67 
    78require 'fm' 
     
    3132# 
    3233class FaerieMUD::GameObject 
    33     include FaerieMUD::Propertied, 
    34         FaerieMUD::Loggable, 
    35         FaerieMUD::Debuggable, 
    36         FaerieMUD::HTML, 
    37         FaerieMUD::Linguistics 
    38  
    39     include MonitorMixin 
     34    include Sync_m, 
     35            FaerieMUD::AbstractClass, 
     36            FaerieMUD::Propertied, 
     37            FaerieMUD::Loggable, 
     38            FaerieMUD::Debuggable, 
     39            FaerieMUD::HTML, 
     40            FaerieMUD::Linguistics 
    4041 
    4142    ### Class constants 
     
    5960 
    6061    # The authors hash for this class 
    61     @authors = Hash::new( 0 ) 
     62    @authors = Hash.new( 0 ) 
    6263    @contributors = [] 
    6364    class << self 
     
    7778    def self::add_authors_data 
    7879        unless self.instance_variables.include?( "@authors" ) 
    79             self.instance_variable_set( :@authors, Hash::new(0) ) 
     80            self.instance_variable_set( :@authors, Hash.new(0) ) 
    8081            self.instance_variable_set( :@contributors, [] ) 
    8182            class << self # :nodoc: 
     
    100101    ### stillflame@FaerieMUD.org for the algorithm. 
    101102    def self::distribute_points( *people ) 
    102         points = Array::new( people.nitems, 1 ) 
     103        points = Array.new( people.nitems, 1 ) 
    103104 
    104105        (PointTotal - people.nitems).times do |n| 
     
    112113        end 
    113114 
    114         chart = Hash::new( 0 ) 
     115        chart = Hash.new( 0 ) 
    115116        people.each_with_index do |person, i| 
    116117            chart[person] += points[i] 
     
    121122 
    122123 
    123     ### Make a unique id for the object that can persist across multiple 
    124     ### invocations (i.e., because #object_id won't). 
    125     def self::make_object_id( obj ) 
    126         context = Digest::MD5::new 
    127         context << "%d" % obj.object_id << 
    128             "%0.5f" % Time::now << 
    129             Thread::current.inspect 
    130  
    131         return context.hexdigest 
    132     end 
    133  
    134  
    135124    ### Versioning method -- return the subversion rev number for the object. 
    136125    def self::rev 
    137         revno = self::const_get( :SVNRev ) 
     126        revno = self.const_get( :SVNRev ) 
    138127        if revno.match( /(\d+)/ ) 
    139128            return Integer($1) 
    140129        else 
    141130            return 0 
    142         end 
    143     end 
    144  
    145  
    146     ### Define a method that will raise a NotImplementedError when 
    147     ### called. This enforces implementation of a particular interface in 
    148     ### subclasses of the defining class. 
    149     def self::abstract( *syms ) 
    150         syms.each do |meth| 
    151             define_method( meth ) do 
    152                 raise NotImplementedError, 
    153                 "No implementation for \#%s provided by %s" % 
    154                 [ meth, self.class.name ] 
    155             end 
    156131        end 
    157132    end 
     
    163138    ### arguments. 
    164139    def self::example( args={} ) 
    165         self::new( args ) 
     140        new( args ) 
    166141    end 
    167142 
     
    179154 
    180155        # Add unique ID, rev, and mutex 
    181         @id = self.class::make_object_id( self ) 
    182         @rev = self.class::rev 
    183         @mutex = Sync::new 
     156        @id = UUID.timestamp_create 
     157        @rev = self.class.rev 
    184158 
    185159        # Call setters for named arguments 
     
    190164 
    191165 
    192     ### Copy initializer -- generate a unique id and mutex for cloned objects. 
     166    ### Copy initializer -- generate a unique id for cloned objects. 
    193167    def initialize_copy( original ) 
    194168        super 
    195         @id = self.class::make_object_id( self ) 
    196         @mutex = Sync::new 
    197         self.log.debug "Cloned #{self.class.name} #{original.id}." 
     169        @id = UUID.timestamp_create 
    198170    end 
    199171 
     
    205177    # The object's unique id 
    206178    attr_reader :id 
     179    alias_method :uuid, :id 
    207180 
    208181    # The revision number of the class from which the object was instantiated at 
    209182    # the time of its creation or last upgrade. 
    210183    attr_reader :rev 
    211  
    212     # The Sync mutex for this object 
    213     attr_reader :mutex 
    214184 
    215185 
     
    225195    def readlocked 
    226196        return unless block_given? 
    227         @mutex.synchronize( Sync::SH ) do 
    228             yield( @mutex ) 
     197        self.synchronize( Sync::SH ) do 
     198            yield 
    229199        end 
    230200    end 
     
    240210    def writelocked 
    241211        return unless block_given? 
    242         @mutex.synchronize( Sync::EX ) do 
    243             yield( @mutex ) 
     212        self.synchronize( Sync::EX ) do 
     213            yield 
    244214        end 
    245215    end 
  • trunk/lib/fm/locus.rb

    r285 r287  
    160160                obj.add_container(self) 
    161161            end 
    162             @mutex.synchronize( Sync::EX ) { @contents |= objects } 
     162            synchronize( Sync::EX ) { @contents |= objects } 
    163163        end 
    164164 
     
    182182 
    183183        # Expand classes into the objects they match 
    184         @mutex.synchronize( Sync::SH ) do 
     184        synchronize( Sync::SH ) do 
    185185            expanded = objects.collect do |obj| 
    186186                if obj.is_a?( Class ) 
     
    195195            removedObjects = @contents & expanded 
    196196            removedObjects.each {|obj| obj.remove_container(self) } 
    197             @mutex.synchronize( Sync::EX ) { @contents -= removedObjects } 
     197            synchronize( Sync::EX ) { @contents -= removedObjects } 
    198198        end 
    199199 
     
    204204    ### Add the specified object to the list of the receiver's containers. 
    205205    def add_container( object ) 
    206         @mutex.synchronize( Sync::SH ) { 
     206        synchronize( Sync::SH ) { 
    207207            if self.contains?( object ) 
    208208                raise FaerieMUD::ContainmentError, 
     
    210210            end 
    211211 
    212             @mutex.synchronize( Sync::EX ) { 
     212            synchronize( Sync::EX ) { 
    213213                @containers |= [object] 
    214214            } 
     
    220220    ### containers. 
    221221    def remove_container( object ) 
    222         @mutex.synchronize( Sync::EX ) { @containers -= [object] } 
     222        synchronize( Sync::EX ) { @containers -= [object] } 
    223223    end 
    224224 
  • trunk/lib/fm/mixins.rb

    r285 r287  
    44 
    55require 'fm/exceptions' 
     6 
    67 
    78# [<tt>FaerieMUD::Propertied</tt>] 
     
    7475 
    7576    end # module Loggable 
     77 
     78 
     79    ### Hides your class's ::new method and adds a method generator called 'virtual' for 
     80    ### defining API methods. If subclasses of your class don't provide implementations of 
     81    ### "virtual" methods, NotImplementedErrors will be raised if they are called. 
     82    ### 
     83    ###   # AbstractClass 
     84    ###   class MyBaseClass 
     85    ###       include ThingFish::AbstractClass 
     86    ### 
     87    ###       # Define a method that will raise a NotImplementedError if called 
     88    ###       virtual :api_method 
     89    ###   end 
     90    ### 
     91    module AbstractClass 
     92 
     93        ### Methods to be added to including classes 
     94        module ClassMethods 
     95 
     96            ### Define one or more "virtual" methods which will raise 
     97            ### NotImplementedErrors when called via a concrete subclass. 
     98            def virtual( *syms ) 
     99                syms.each do |sym| 
     100                    define_method( sym ) { 
     101                        raise ::NotImplementedError, 
     102                            "%p does not provide an implementation of #%s" % [ self.class, sym ], 
     103                            caller(1) 
     104                    } 
     105                end 
     106            end 
     107 
     108 
     109            ### Turn subclasses' new methods back to public. 
     110            def inherited( subclass ) 
     111                subclass.module_eval { public_class_method :new } 
     112                super 
     113            end 
     114 
     115        end # module ClassMethods 
     116 
     117 
     118        extend ClassMethods 
     119 
     120        ### Inclusion callback 
     121        def self::included( mod ) 
     122            super 
     123            if mod.respond_to?( :new ) 
     124                mod.extend( ClassMethods ) 
     125                mod.module_eval { private_class_method :new } 
     126            end 
     127        end 
     128 
     129 
     130    end # module AbstractClass 
    76131 
    77132 
     
    524579                define_method( sym ) { 
    525580                    if instance_variables.include?( ivarname ) 
    526                         @mutex = Sync::new unless defined? @mutex 
    527                         @mutex.synchronize( Sync::SH ) { 
     581                        readlocked do 
    528582                            instance_variable_get( ivarname ) 
    529                         } 
     583                        end 
    530584                    else 
    531585                        nil 
     
    537591            if writer 
    538592                define_method( "#{sym}=".intern ) {|arg| 
    539                     @mutex = Sync::new unless defined? @mutex 
    540                     @mutex.synchronize( Sync::EX ) { 
     593                    writelocked do 
    541594                        instance_variable_set( ivarname, arg ) 
    542                     } 
     595                    end 
    543596                } 
    544597            end 
     
    568621 
    569622 
    570     ### A collection of rudimentary functions which are useful for generating 
    571     ### HTML without a lot of string-catenating, etc. 
     623    ### Adds the #to_html method to including classes, which can dump the description of an  
     624    ### object as HTML. 
     625    ###  
     626    ### == Customization 
     627    ### You can customize the HTML generated for your class by overriding one of the following 
     628    ### protected methods: 
     629    ###  
     630    ### #html_object_header, #html_object_footer, #html_ivar_section, #html_wrapper 
    572631    module HTML 
     632 
     633        require 'fm/linguistics' 
     634        require 'fm/gameobject' 
    573635 
    574636        ###### 
     
    577639 
    578640        ### Return the object as one or more HTML fragments. If +inline+ is 
    579         ### +true+, the result will be wrapped in a <span> element suitable for 
    580         ### inline display. If it is +false+, it will be wrapped in a <div> 
     641        ### +true+, the result will be wrapped in a SPAN element suitable for 
     642        ### inline display. If it is +false+, it will be wrapped in a DIV 
    581643        ### element for block display. 
    582644        def to_html( inline=false ) 
     
    604666            return func.call( :class => "object-header" ) { 
    605667                [ 
    606                     FaerieMUD::HTML::span( :class => "object-noun" ) { 
     668                    FaerieMUD::HTML.span( :class => "object-noun" ) { 
    607669                        FaerieMUD::Linguistics::make_noun( self ) 
    608670                    }, 
    609                     FaerieMUD::HTML::span( :class => "object-class" ) { 
     671                    FaerieMUD::HTML.span( :class => "object-class" ) { 
    610672                        self.class.name 
    611673                    }, 
    612                     FaerieMUD::HTML::span( :class => "object-id" ) { 
    613                         "#%s" % self.id 
     674                    FaerieMUD::HTML.span( :class => "object-id" ) { 
     675                        "#%s" % self.object_id 
    614676                    }, 
    615                     FaerieMUD::HTML::span( :class => "object-class-version" ) { 
    616                         "(v%d)" % self.class.rev 
     677                    FaerieMUD::HTML.span( :class => "object-class-version" ) { 
     678                        self.class.respond_to?( :rev ) ? "(v%d)" % self.class.rev : '(unknown)' 
    617679                    }, 
    618680                ].join(" ") 
     
    624686        def html_object_footer( inline ) 
    625687            func = inline ? 
    626                 FaerieMUD::HTML::method(:span) : 
    627                 FaerieMUD::HTML::method(:div) 
    628  
    629             return func.call( :class => "object-footer" ) { 
    630                 FaerieMUD::HTML::span( :class => "object-contrib" ) { 
     688                FaerieMUD::HTML.method(:span) : 
     689                FaerieMUD::HTML.method(:div) 
     690 
     691            contents = [] 
     692 
     693            if self.class.respond_to?( :authors ) 
     694                contents << FaerieMUD::HTML.span( :class => "object-contrib" ) do 
    631695                    "Authors: " + 
    632                     self.class.authors.collect {|author,points| 
    633                         FaerieMUD::HTML::span( 
     696                    self.class.authors.collect do |author,points| 
     697                        FaerieMUD::HTML.span( 
    634698                            :class => "object-contrib-author" ) {author.to_s} + 
    635699                        ": " + 
    636                         FaerieMUD::HTML::span( 
     700                        FaerieMUD::HTML.span( 
    637701                            :class => "object-contrib-points" ) {points.to_s} 
    638                     }.join("; ") 
    639                 } 
    640             } 
    641  
     702                    end.join("; ") 
     703                end 
     704            end 
     705             
     706            if self.class.const_defined?( :SVNId ) 
     707                contents << FaerieMUD::HTML.span( :class => 'object-svn-id' ) do 
     708                    self.class.const_get(:SVNId) 
     709                end 
     710            end 
     711 
     712            return func.call( :class => "object-footer" ) { contents } 
    642713        end 
    643714