require 'date' require 'ostruct' # A lot of our data coming out of the server has a couple of issues: # # - The ID of an object might be referenced by different cases, or different # field names # # - Dates are sometimes as returned as timestamp strings (e.g., "1437153920") # and sometimes as rendered strings (e.g., "2015/07/17 10:36:38") # # This is intended to be mixed in with an OpenStruct class to let your code # handle conversion as needed. # # You have a couple of rules: # # 1. For [name], if a capitalized version exists we return that # # 2. For [name], if a camel case version of the name exists we return that # # 3. You can use an "[method]_as_time" to try to detect the format and return # it as a ruby Time instance. # # * You must provide an 'offset' parameter here based on the serverTime # value from p4 info. # # 4. You can use an "[name]_or_[name]" to fallback to a different symbols. # # rev_or_revision # desc_or_description # # For example, say you have the Hash # # { # 'branch': 'my_branch', # 'Update': "1437153920" # } # # Then your object should both respond to: # # branch.branch # branch.update_as_time(offset) # # This only affects "read" methods. Your code should probably only use the # Singular format for "write" methods, and rarely should it ever need to # create dates. class OpenModel < OpenStruct class << self def capitalize(sym) sym.to_s.split('_').map{ |e| e.capitalize }.join end def camelize(sym) sym.to_s.split('_').inject([]){ |buffer,e| buffer.push(buffer.empty? ? e : e.capitalize) }.join end def date_str?(str) Date.parse(str) rescue false end end def method_missing(sym, *args) match_time = /^(.*)_as_time$/.match(sym.to_s) if match_time val = send(match_time[1]) if val =~ /\A[-+]?\d+\z/ return Time.at(val.to_i) elsif OpenModel.date_str?(val) && args.length > 0 offset = args.first return DateTime.parse("#{val} #{offset}").to_time end end mname = sym.id2name is_assign = mname.chomp!('=') if mname.split('_or_').length > 1 syms = mname.to_s.split('_or_') s = syms.find { |s| @table.key?(OpenModel.capitalize(s).to_sym) } if s mname = s.to_s else s = syms.find { |s| @table.key?(OpenModel.camelize(s).to_sym) } mname = s.to_s if s end end if @table.key?(OpenModel.capitalize(mname).to_sym) mname = OpenModel.capitalize(mname) end if @table.key?(OpenModel.camelize(mname).to_sym) mname = OpenModel.camelize(mname) end len = args.length if is_assign if len != 1 raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1) end modifiable[new_ostruct_member(mname)] = args[0] elsif len == 0 @table[mname.to_sym] else err = NoMethodError.new "undefined method `#{mname.to_sym}' for #{self}", mname.to_sym, args err.set_backtrace caller(1) raise err end end # Note: I'm really unsure of exactly how respond_to? should be used here. end
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 15741 | ptomiak | Branch HWS for my use. | ||
//guest/perforce_software/helix-web-services/main/source/helix_web_services_client/lib/helix_web_services_client/open_model.rb | |||||
#1 | 15622 | tjuricek |
Move source code to 'source/' subdirectory of branch. build/ will remain where it is. |
||
//guest/perforce_software/helix-web-services/main/helix_web_services_client/lib/helix_web_services_client/open_model.rb | |||||
#2 | 15185 | tjuricek | Update user spec management implementation, tests, and documentation. | ||
#1 | 15077 | tjuricek |
Add new 'model' technique, revised branch spec operations, test Auth::Middleware. The Ruby client now does *not* strictly type anything, but extends OpenStruct with helper methods to help deal with inconsistent data formats. See the OpenModel class documentation for more details. The Auth::Middleware class is also *finally* implemented as well. This does not take into account all possible variations of server behavior (yet), but that will happen in follow-up work. |