lib/rom/struct.rb in rom-repository-1.3.2 vs lib/rom/struct.rb in rom-repository-1.3.3

- old
+ new

@@ -5,18 +5,25 @@ # # ROM structs are plain data structures loaded by repositories. # They implement Hash protocol which means that they can be used # in places where Hash-like objects are supported. # - # Repositories define subclasses of ROM::Struct automatically, they are not - # defined as constants in any module, instead, generated mappers are configured - # to use anonymous struct classes as models. + # Repositories define subclasses of ROM::Struct automatically, they are + # defined in the ROM::Struct namespace by default, but you set it up + # to use your namespace/module as well. # # Structs are based on dry-struct gem, they include `schema` with detailed information # about attribute types returned from relations, thus can be introspected to build # additional functionality when desired. # + # There is a caveat you should know about when working with structs. Struct classes + # have names but at the same time they're anonymous, i.e. you can't get the User struct class + # with ROM::Struct::User. ROM will create as many struct classes for User as needed, + # they all will have the same name and ROM::Struct::User will be the common parent class for + # them. Combined with the ability to provide your own namespace for structs this enables to + # pre-define the parent class. + # # @example accessing relation struct model # rom = ROM.container(:sql, 'sqlite::memory') do |conf| # conf.default.create_table(:users) do # primary_key :id # column :name, String @@ -28,25 +35,45 @@ # # user_repo = UserRepo.new(rom) # # # get auto-generated User struct # model = user_repo.users.mapper.model - # # => ROM::Struct[User] + # # => ROM::Struct::User # # # see struct's schema attributes # # # model.schema[:id] # # => #<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=Integer options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#gt?> options={:args=>[0]}>, :meta=>{:primary_key=>true, :name=>:id, :source=>ROM::Relation::Name(users)}} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#gt?> options={:args=>[0]}>> # # model.schema[:name] # # => #<Dry::Types::Sum left=#<Dry::Types::Constrained type=#<Dry::Types::Definition primitive=NilClass options={}> options={:rule=>#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>} rule=#<Dry::Logic::Rule::Predicate predicate=#<Method: Module(Dry::Logic::Predicates::Methods)#type?> options={:args=>[NilClass]}>> right=#<Dry::Types::Definition primitive=String options={}> options={:meta=>{:name=>:name, :source=>ROM::Relation::Name(users)}}> # + # @example passing a namespace with an existing parent class + # module Entities + # class User < ROM::Struct + # def upcased_name + # name.upcase + # end + # end + # end + # + # class UserRepo < ROM::Repository[:users] + # struct_namespace Entities + # end + # + # user_repo = UserRepo.new(rom) + # user = user_repo.users.by_pk(1).one! + # user.name # => "Jane" + # user.upcased_name # => "JANE" + # # @see http://dry-rb.org/gems/dry-struct dry-struct # @see http://dry-rb.org/gems/dry-types dry-types # # @api public class Struct < Dry::Struct + MissingAttribute = Class.new(NameError) + # Returns a short string representation # # @return [String] # # @api public @@ -63,17 +90,13 @@ __send__(name) end private - def method_missing(m, *args) - inspected = inspect - trace = caller - - # This is how MRI currently works - # see func name_err_mesg_to_str in error.c - name = inspected.size > 65 ? to_s : inspected - - raise NoMethodError.new("undefined method `#{ m }' for #{ name }", m, args).tap { |e| e.set_backtrace(trace) } + def method_missing(method, *) + super + rescue NameError => error + raise if method == :to_ary + raise MissingAttribute.new("#{ error.message } (not loaded attribute?)") end end end