Giter Site home page Giter Site logo

rom-rb / rom Goto Github PK

View Code? Open in Web Editor NEW
2.1K 75.0 161.0 8.2 MB

Data mapping and persistence toolkit for Ruby

Home Page: https://rom-rb.org

License: MIT License

Ruby 99.78% Shell 0.03% Dockerfile 0.02% HTML 0.16%
ruby databases data-access-layer sql nosql toolkit rom-rb ddd persistence-toolkit data-mapping

rom's Introduction

rom Join the chat at https://rom-rb.zulipchat.com

Gem Version CI Status Codacy Badge Codacy Badge Inline docs OpenCollective OpenCollective

Ruby Object Mapper (rom-rb) is a data mapping and persistence toolkit for Ruby with the goal to provide powerful object mapping capabilities without limiting the full power of your database.

Main rom gem provides following components:

  • core - Core and Adapter APIs
  • changeset - Changeset objects integrated with rom-core
  • repository - Additional repository abstraction integrated with rom-core

Learn more:

Backers

Support us with a monthly donation and help us continue our activities. [Become a backer]

Sponsors

Become a sponsor and get your logo on our README on Github with a link to your site. [Become a sponsor]

Ecosystem

There are other gems within the rom ecosystem that you will find useful:

Adapters

Framework integrations

Community

Credits

This project has a long history and wouldn't exist without following people:

License

See LICENSE file.

rom's People

Contributors

aflatter avatar alsemyonov avatar amhol avatar astupka avatar cflipse avatar chastell avatar cored avatar dcarral avatar dkubb avatar dreoliv avatar elskwid avatar endash avatar errinlarsen avatar flash-gordon avatar gotar avatar gustavocaso avatar hmadison avatar maetl avatar mbj avatar mcls avatar mjtko avatar nepalez avatar plexus avatar rom-bot avatar snusnu avatar solnic avatar splattael avatar v-kolesnikov avatar vrish88 avatar waiting-for-dev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rom's Issues

Define interfaces for accessing schema, relations and mappers

Currently env object gives access to all the registries:

# current registry objects
env.schema # returns schema registry with *base relations*
env.mappers # returns registry with mappers for *base relations*
env.relations # returns registry with "public relations" that should be used directly on the application level

# accessing individual objects inside registries works with either [](name) or .name
env.schema.users == env.schema[:users]
env.mappers.users == env.mappers[:users]
env.relations.users == env.relations[:users]

# relation registry accepts additional options hash
env.relations.users(mapper: true) # this will wrap the relation with a mapper object so the relation will yield models rather than hashes

Here's a couple of potential improvements:

  • make it possible to access relations directly via env object ie env.users since that's the top level public relation registry we want to use on the application level
  • make Env#mappers private and come up with a better interface for accessing mapped relations (I don't like env.relations.users(mapper: true)

Improve how headers are handled

  • add a sane interface to Header class so that accessing attribute information is simple
  • add Header::Attribute and subclasses for primitive and embedded types
  • add #header to adapter and in-memory RA layers so that it is always possible to ask for the header attributes
  • simplify how attributes are handled in mapper/reader/model builders thanks to the improved header interface

Data manipulation interface

For now ROM will simply leak the underlaying data manipulation interface coming with the adapter. For instance in case of Sequel you can simply use insert, update and delete and it's your job to encapsulate that somehow. I can imagine that it should be possible to come up with some conventions for dealing with data manipulation using adapters so that it's easier to achieve nice encapsulation. Time will tell.

I'm reporting this issue so that we can have a place for everyone to chime in and discuss various strategies.

Rom doesn't do auto mapping on restriction?

Hello team,

Today I tried to use Rom 0.2.0 and I found that we have to do manual mapping to map attributes:

env[:users].restrict(id: 961781).rename(:id => :accountid).one

Before, that upgrade it worked without explicit calling the rename method, now it is required to get a proper field name in DB.

Is there any possibility to get auto-mapping while defining restriction?

Thank you for your hard work!!!

Schema attributes

Hello there!

I have a question about attributes
We have a schema

env.schema do
  base_relation :users do
    repository :memory

    attribute :id,   Integer
    attribute :name, String

    key :id
  end
end

Is there a way to take the attributes and pass them to the model? Because we can have a lot of attributes

class User
  def initialize(attributes)
    @id, @name = attributes.values_at(:id, :name)
  end
end

Or whether in ROM support of Virtus for extend model?

Add support for defining context-aware relations

This means something more or less like this (DSL ideas are welcome!):

rom.schema do
  base_relation(:users) do
    repository :sqlite

    attribute :user_id, Integer
    attribute :name, String
    attribute :country, String
  end

  users do
    def from_country(code)
      filter(country: code)
    end
  end
end

rom.relations.users.from_country("PL")

Error Message When Running Example

When I require the code from the example on the ReadMe page in IRB, I get the following error:

NameError: undefined local variable or method `include_comparison_methods' for #Axiom::Equalizer:0x927235c

Do I need to change anything?

DSL for in-memory relations

Here's an idea I have how we could define in-memory relations that can span multiple repositories:

rom = ROM.setup(sqlite: 'sqlite::memory', redis: 'redis://localhost') do
  schema do
    base_relation(:posts) do
      repository :sqlite

      attribute :post_id, Integer
      attribute :title, String
    end

    base_relation(:tags) do
      repository :redis

      attribute :post_id, Integer
      attribute :tags, Array
    end

    relation(:posts_with_tags) do
      join(posts, tags)
    end
  end
end

rom.relations.posts_with_tags # this would return joined relation

Add a way to extend a relation with adapter-specific helper interfaces

Building complex queries with adapters could be simplified using higher-level interfaces. For example when using Sequel adapter we could have a set of methods for easier joins where details like correct column names and join keys are being set automatically.

It is, however, a rabbit hole in the long term but it's something to consider nevertheless. I believe if we narrow down the functionality to the minimum we could have something that's reliable.

Here's an example of what I'm talking about:

# let's say we have users and tasks relations that we can join
rom.relations do
   users do
     # some index view where we need users and tasks together
     def index
       # with a simplified interface via helpers it *could* look like that
       users.join(tasks)

       # without it there's a lot of things we need to take care of
       users.join(tasks, user_id: Sequel.qualify(:users, :id)).
         select(some_nasty_code_generating_the_list_of_column_names)
     end
   end
end

No documentation for associations/foreign key joins

The roadmap says that associations should be supported, but I can't find any documentation on how to do more than one model in one repository in any of the examples. How do I declare associations and/or foreign keys for joins? The last code I could find was this: solnic/rom-relation@38136a8 , but there's no explanation of what I use in place of has from before that commit. Are association still pending work? I would like to use rom to allow in-memory emulation of https://github.com/rapid7/metasploit_data_models when the user doesn't have access to and/or want to setup postgres. If you feel that rom is missing too many features at this time to do this, please let me know.

ROM doesn't work with relation database

Hello team,

I tried to use ROM with simple Sqlite database, but it doesn't work. My code:

require 'rom-relation'
require 'rom-mapper'
require 'rom/support/axiom/adapter/sqlite3'

rom = ROM::Environment.setup(sqlite3: 'sqlite3://test.sql')

rom.schema do
  base_relation :users do
    repository :sqlite3

    attribute :id,   Integer
    attribute :name, String
  end
end

class User
  attr_accessor :id, :name
end

rom.mapping do
  repos do
    model User

    map :id, :name
  end
end

puts rom[:users].to_a.inspect

The error which I receive:

/home/dnesteryuk/.rvm/gems/ruby-1.9.3-p429/gems/rom-relation-0.1.2/lib/rom/repository.rb:56:in `[]=': undefined method `[]=' for #<Axiom::Adapter::Sqlite3:0x00000001bc4a48> (NoMethodError)
    from /home/dnesteryuk/.rvm/gems/ruby-1.9.3-p429/gems/rom-relation-0.1.2/lib/rom/schema/definition.rb:38:in `base_relation'
    from test.rb:8:in `block in <main>'
    from /home/dnesteryuk/.rvm/gems/ruby-1.9.3-p429/gems/rom-relation-0.1.2/lib/rom/schema.rb:43:in `instance_eval'
    from /home/dnesteryuk/.rvm/gems/ruby-1.9.3-p429/gems/rom-relation-0.1.2/lib/rom/schema.rb:43:in `call'
    from /home/dnesteryuk/.rvm/gems/ruby-1.9.3-p429/gems/rom-relation-0.1.2/lib/rom/environment.rb:66:in `schema'
    from test.rb:7:in `<main>'

Should it work? Or may be it is not supported yet?

Collapse relation, mapper and session into a single repo

Refactoring in rom-mapper made me realize it's really a good idea to collapse all 3 pieces into a single repo as there are still way too many moving parts and I find it a PITA to manage 4 projects separately. It would also give better visibility of what you are breaking when introducing refactorings like in this branch (it breaks integration with both rom-relation and rom-session). I also don't think anybody will use session or mapper or relation as standalone gems, honestly. ROM is clearly becoming a convenient DSL on top of lower-level tools (axiom, morpher, probably more will come) so I don't see any point in maintaining complexity that comes with the separation. It is also way easier for people to understand the project when it's in a single repo. We can always extract things later when we see that it makes sense (I don't see any sense at the moment).

Add support for coercions

Currently mapper definition interface only allows to specify what attributes should be mapped. It would be nice to be able to specify coercions too. How those coercions are going to be handled will depend on the model builder type. In case of PORO we could have something trivial like relying on built-in coercion methods in ruby, for Virtus builder we can simply use its type-definition system, more Morpher we should construct nodes that provide coercions (this is still a WIP in Morpher though) and so on.

The interface could look like that:

rom.mappers do
  define(:users) do
    attribute :_id, type: :string
  end
end

Add relation#insert and mapper#insert

A common pattern is to insert a tuple into a relation and get an object back. In case of Sequel adapter we get the PK value back so a sequel mapper should be able to build a newly persisted object and return it back:

env.relations.users.insert(name: "Jane") # this would return the tuple with PK set
env.relations.users(mapper: true).insert(name: "Jane") # this would return an instance of User model

ROM does not save using axiom-do-adapter

I have the following environment defined (the same as the example):

require 'rom'
require 'axiom-do-adapter'
require 'do_sqlite3'

class User
  attr_reader :id, :name

  def initialize(attributes)
    @id, @name = attributes.values_at(:id, :name)
  end
end

env = ROM::Environment.setup(sqlite: 'sqlite3://#{Rails.root}/db/development.sqlite3') do
  schema do
    base_relation :users do
      repository :sqlite

      attribute :id,   Integer
      attribute :name, String

      key :id
    end
  end

  mapping do
    users do
      map :id, :name
      model User
    end
  end
end

I then try to use a session to insert a new record.

env.session do |session|
  user = session[:users].new(id: 1, name: 'Jane')
  session[:users].save(user)
  session.flush
end

This does not error out but the record is never saved to the sqlite database. I tried the saving a slot with the example repo but no luck either.

Is this feature supported?

Investigate possible performance problems

Here are results of benchmarks/basic.rb

rom-rb/rom (master«) % COUNT=100 benchmarks/basic.rb
       user     system      total        real
seed  0.050000   0.010000   0.060000 (  0.047423)
delete  0.250000   0.000000   0.250000 (  0.254174)
seed and delete  0.330000   0.000000   0.330000 (  0.336303)


rom-rb/rom (master«) % COUNT=200 benchmarks/basic.rb
       user     system      total        real
seed  0.130000   0.000000   0.130000 (  0.130733)
delete  1.030000   0.010000   1.040000 (  1.033373)
seed and delete  1.170000   0.000000   1.170000 (  1.176047)


rom-rb/rom (master«) % COUNT=400 benchmarks/basic.rb
       user     system      total        real
seed  0.360000   0.010000   0.370000 (  0.361748)
delete  4.130000   0.010000   4.140000 (  4.140899)
seed and delete  4.540000   0.010000   4.550000 (  4.549963)

It seems like this grows exponentially. I'm attaching perftools results with focus on Axiom.

rom_profile

Write a nice README

The README should explain the philosophy behind the project, main differences between ROM and other typical ruby ORMs and show how to quickly get started with ROM showing some basic examples. It should also have a nice CREDITS section mentioning DM2, Axiom and people behind all the initial efforts.

/cc @elskwid HELP NEEDED HERE :)

Add support for "wrap" operation

This is also known as "embedded objects":

rom.relations do
  users do
    def with_address
      natural_join(addresses)
    end
  end
end

rom.mappers do
  define(:users) do
    model name: 'User'

    attribute :name

    wrap address: [:street, :zipcode, :city]
  end
end

Add migration layer/library

There should be some sort of either explicit and/or automatic migration support. Ideally with the following features:

  1. Ability to easily define migrations via a DSL syntax.
    • Syntax should be easy enough to define one-off migrations in irb or define complex migrations for db/migrations/.
  2. Ability to convert ROM Schema objects into adapter specific schema objects.
    • Example of this would be mapping ROM attribute types to adapter specific types, and defining which attributes need keys and/or indexes.
  3. Ability to query and load schema information from the adapter.
    • This would be useful for diffing the ROM schema against the actual current schema and generating a set of auto-migrations, which would either be executed or dumped out into db/migrations/.

Add a new DSL for defining mappers

Initially let's just support model.new(tuple) and we can extend it in the future versions to support more advanced mapping with coercions and whatnot.

Add support for defining a model for group/wrap operations

For consistency this should be similar to model definition interface:

rom.mappers do
  define(:with_tasks, parent: users) do
    group :tasks do
      model name: 'Task'
    end
  end

  define(:with_address, parent: users) do
    wrap :address do
      model name: 'Address'
    end
  end
end

Improve and extend mapper definition DSL

Main motivation behind this DSL is to have a simple way of defining how relations are mapped to ruby objects. I'd like to make it as concise as possible and automate as much as possible. I can imagine implementing entity classes is a tedious thing to do and personally I'd prefer ROM to generate them for me, however, I can also see the need for having an ability to re-open those classes to add some behavior hence defining constants might be a good idea.

Notice that with a data mapper you can't have a single User model because you often may need a user object that's an aggregate with some child objects and for that you need a separate model. My gut feeling is telling me ROM will move us towards behavior-less entity objects that are simple "data capsules" and nothing more, we'll see.

Here's a draft of what I'm thinking about:

# assuming we have a base relation called :users with attributes :id, :name, :email
rom.mappers do

  users do
    # possible to explicitly set model class
    model User
    # or tell rom to generate it for us

    model name: 'Entities::User', type: :virtus
    # with support for different model types, like anima, poro etc. whatever we want

    # we can define common mappings for all relations in the root
    map :id, :name, :email

    # assuming we have a users.with_tasks relation
    with_tasks do
      group tasks: [:title, :priority]
    end

    # in sql-based relations it is common to prefix attribute names to avoid name clashes
    # so we can tell the mapper about it
    with_tasks(prefix: true) do
      group tasks: [:title, :priority]
    end

  end
end

This DSL is mostly inspired by the previous work on ROM and @snusnu's ramom.

Unify and DRY-up registry objects

There's a lot of duplication in schema, relation and mapper registry classes. Would be nice to come up with a common abstraction and use inheritance here to dry-up this mess.

Demo example

I'm trying to use demo example from the webpage(http://rom-rb.org/).

But looks like the only version of rom released is 0.0.1 and package is empty except LICENSE and README.md files.

Using git repos in Gemfile also didn't help:

gem 'rom-relation' , :git => 'https://github.com/rom-rb/rom-relation.git'
gem 'rom-mapper'   , :git => 'https://github.com/rom-rb/rom-mapper.git'
gem 'rom-session'  , :git => 'https://github.com/rom-rb/rom-session.git'
gem 'rom'          , :git => 'https://github.com/rom-rb/rom.git'
bundle install
...
There was a LoadError while loading rom.gemspec: 
cannot load such file -- rom-relation from
  /home/me/.rvm/gems/ruby-2.0.0-p247@rom-test/bundler/gems/rom-68daa8cebb1d/rom.gemspec:2:in `<main>'

Does it try to require a relative path? That's been removed in Ruby 1.9.

It would be good to have some wiki with HOWTO to setup development environment.

Add model builder

Writing model classes is tedious. When you have a schema in place in most of the cases you can and you probably want to simply get the model classes generated for you.

Add support for tuples with string keys

Some database drivers return tuples with string keys, like moped for example. This could affect the model builder if it expects symbolized tuples. This could be improved by either symbolizing keys before returning the tuples from relation (configurable on the adapter level) or tweaking model builders so that they can support either symbolized or stringified keys.

Improve how adapters are being loaded

  • every adapter should register itself so that finding an adapter class based on scheme doesn't require a gigantic case statement, something like Adapter.register(self) would do the trick
  • add an identifier to every adapter sub-class so that we can easily find correct class based on uri's schema (something like Adapter::Sequel.schema that returns an array of supported schemas like ['sqlite', 'postgres', ...] etc. and then something like Adapter[schema] would return a class for the schema (by going through registered adapter classes)
  • dynamically require adapter file (right now all adapters are being required which is silly)

rom-relation bug with axiom

I just install latest rom gem and receive this strange error after requiring 'rom' in irb

NameError: undefined local variable or method `include_comparison_methods' for #<Axiom::Equalizer:0x00000002419c48>
from /home/valikos/.rbenv/versions/1.9.3-p484/lib/ruby/gems/1.9.1/gems/axiom-0.1.1/lib/axiom/support/equalizer.rb:18:in `initialize'

I'm confused because I am watching on installed source code of axiom-0.1.1/lib/axiom/support/equalizer.rb file

def initialize(*keys)
  @keys = keys
  define_methods
  include_comparison_methods

  module_eval do
    include Adamantium
    memoize :hash
  end

  freeze
end

and source on github

def initialize(*)
  super
  module_eval do
    include Adamantium
    memoize :hash
  end
end

I'm have this problem on three different workstations.
Sorry if this problem does not apply to rom project, because it is related with axiom.

Add support for renaming attributes

A common mapper feature is to rename an attribute in the result tuple. Interface idea:

rom.mappers do
  # let's say this is mongo so we get :_id in the tuple
  # and would like to rename it to :id
  define(:users) do
    attribute :id, from: :_id
  end
end

An array of elements as an attribute

Before you had renamed this project to Rom, I had possibility to use Virtus which provides very handy features:

class Book
  include Virtus.model

  attribute :page_numbers, Array[Integer]
end

Do we have such possibility now with Rom? Unfortunately, now we have to describe attributes in a schema. It means we cannot use Virtus anymore, only attributes provided by Rom/Axiom.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.