From 30d2823571fcc1f30ba707b605ac2df5e030df9a Mon Sep 17 00:00:00 2001 From: Giallombardo Nathan Date: Thu, 20 Jun 2024 10:20:54 +0200 Subject: [PATCH] update readme and licence --- LICENSE | 5 +- README.md | 392 ++++++------------------------------------------------ 2 files changed, 39 insertions(+), 358 deletions(-) diff --git a/LICENSE b/LICENSE index 260fea7a..4976f52c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,5 @@ +Copyright (c) 2023-Present Woop. +Copyright (c) 2021-2022 Mapotempo. Copyright (c) 2016 ACAProjects Permission is hereby granted, free of charge, to any person obtaining a copy @@ -19,6 +21,3 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. === - -This license applies to all parts of the libcouchbase gem (Ruby FFI bindings for libcouchbase only) -Libcouchbase itself [is using the Couchbase license](https://github.com/couchbase/libcouchbase/blob/master/LICENSE) \ No newline at end of file diff --git a/README.md b/README.md index 6dbaf15c..23c74472 100644 --- a/README.md +++ b/README.md @@ -1,369 +1,51 @@ -# Couchbase ORM for Rails +CouchbaseOrm +============ -[![Build Status](https://secure.travis-ci.org/acaprojects/couchbase-orm.svg)](http://travis-ci.org/acaprojects/couchbase-orm) +CouchbaseOrm is an ODM (Object-Record Mapper) framework for Couchbase in Ruby. -## Rails integration +Documentation +------------- -To generate config you can use `rails generate couchbase_orm:config`: +CouchbaseOrm has [extensive user documentation](https://mapotempo.github.io/couchbase-orm/). - $ rails generate couchbase_orm:config dev_bucket dev_user dev_password - => create config/couchbase.yml +CouchbaseOrm is built on top of the Couchbase Ruby Client which has +[its own user documentation](https://docs.couchbase.com/ruby-sdk/current/hello-world/overview.html). -It will generate this `config/couchbase.yml` for you: +Compatibility +------------- -```yaml - common: &common - connection_string: couchbase://localhost - username: dev_user - password: dev_password +CouchbaseOrm supports and is tested against: - development: - <<: *common - bucket: dev_bucket +- MRI 2.7 - 3.1 +- Couchbase server 6.5.5 - 7.6.0 - test: - <<: *common - bucket: dev_bucket_test +Issues +------ - # set these environment variables on your production server - production: - connection_string: <%= ENV['COUCHBASE_CONNECTION_STRING'] %> - bucket: <%= ENV['COUCHBASE_BUCKET'] %> - username: <%= ENV['COUCHBASE_USER'] %> - password: <%= ENV['COUCHBASE_PASSWORD'] %> -``` +Please use the [CouchbaseOrm project](https://github.com/Mapotempo/couchbase-orm/issues/) +in CouchbaseOrm github issues to report issues with CouchbaseOrm. -## Setup without Rails +License +------- -If you are not using Rails, you can configure couchbase-orm with an initializer: +Copyright (c) 2023-Present Woop. +Copyright (c) 2021-2022 Mapotempo. +Copyright (c) 2016 ACAProjects -```ruby -# config/initializers/couchbase_orm.rb -CouchbaseOrm::Connection.config = { - connection_string: "couchbase://localhost" - username: "dev_user" - password: "dev_password" - bucket: "dev_bucket" -} -``` +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: -Views are generated on application load if they don't exist or mismatch. -This works fine in production however by default in development models are lazy loaded. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. - # config/environments/development.rb - config.eager_load = true - -## Examples - -```ruby - require 'couchbase-orm' - - class Post < CouchbaseOrm::Base - attribute :title, :string - attribute :body, :string - attribute :draft, :boolean - end - - p = Post.new(id: 'hello-world', - title: 'Hello world', - draft: true) - p.save - p = Post.find('hello-world') - p.body = "Once upon the times...." - p.save - p.update(draft: false) - Post.bucket.get('hello-world') #=> {"title"=>"Hello world", "draft"=>false, - # "body"=>"Once upon the times...."} -``` - -You can also let the library generate the unique identifier for you: - -```ruby - p = Post.create(title: 'How to generate ID', - body: 'Open up the editor...') - p.id #=> "post-abcDE34" -``` - - - -## Typing - -The following types have been tested : - -- :string -- :integer -- :float -- :boolean -- :date -- :datetime (stored as iso8601, use precision: n to store more decimal precision) -- :timestamp (stored as integer) -- :encrypted - - see - - You must store a string that can be encoded in json (not binary data), use base64 if needed -- :array (see below) -- :nested (see below) - -You can register other types in ActiveModel registry : - -```ruby - class DateTimeWith3Decimal < CouchbaseOrm::Types::DateTime - def serialize(value) - value&.iso8601(3) - end - end - - ActiveModel::Type.register(:datetime3decimal, DateTimeWith3Decimal) -``` - -## Validations - -There are all methods from ActiveModel::Validations accessible in -context of rails application. You can also enforce types using ruby -[conversion methods](http://www.virtuouscode.com/2012/05/07/a-ruby-conversion-idiom/) - -```ruby - class Comment < Couchbase::Model - attribute :author, :string - attribute :body, :string - - validates_presence_of :author, :body - end -``` - -## Views (aka Map/Reduce indexes) - -Views are defined in the model and typically just emit an attribute that -can then be used for filtering results or ordering. - -```ruby - class Comment < CouchbaseOrm::Base - attribute :author :string - attribute :body, :string - view :all # => emits :id and will return all comments - view :by_author, emit_key: :author - - # Generates two functions: - # * the by_author view above - # * def find_by_author(author); end - index_view :author - - # You can make compound keys by passing an array to :emit_key - # this allow to query by read/unread comments - view :by_read, emit_key: [:user_id, :read] - # this allow to query by view_count - view :by_view_count, emit_key: [:user_id, :view_count] - - validates_presence_of :author, :body - end -``` - -You can use `Comment.find_by_author('name')` to obtain all the comments by -a particular author. The same thing, using the view directly would be: -`Comment.by_author(key: 'name')` - -When using a compound key, the usage is the same, you just give the full key : - -```ruby - Comment.by_read(key: '["'+user_id+'",false]') # gives all unread comments for one particular user - - # or even a range ! - - Comment.by_view_count(startkey: '["'+user_id+'",10]', endkey: '["'+user_id+'",20]') - - # gives all comments that have been seen more than 10 times but less than 20 -``` - -Check this couchbase help page to learn more on what's possible with compound keys : - -Ex : Compound keys allows to decide the order of the results, and you can reverse it by passing `descending: true` - -## N1ql - -Like views, it's possible to use N1QL to process some requests used for filtering results or ordering. - -```ruby - class Comment < CouchbaseOrm::Base - attribute :author, :string - attribute :body, :string - n1ql :by_author, emit_key: :author - - # Generates two functions: - # * the by_author view above - # * def find_by_author(author); end - index_n1ql :author - - # You can make compound keys by passing an array to :emit_key - # this allow to query by read/unread comments - n1ql :by_read, emit_key: [:user_id, :read] - # this allow to query by view_count - n1ql :by_view_count, emit_key: [:user_id, :view_count] - - validates_presence_of :author, :body - end -``` - -## Basic Active Record like query engine - -```ruby -class Comment < CouchbaseOrm::Base - attribute :title, :string - attribute :author, :string - attribute :category, :string - attribute :ratings, :number -end - -Comment.where(author: "Anne McCaffrey", category: ['S-F', 'Fantasy']).not(ratings: 0).order(:title).limit(10) - -# Relation can be composed as in AR: - -amc_comments = Comment.where(author: "Anne McCaffrey") - -amc_comments.count - -amc_sf_comments = amc_comments.where(category: 'S-F') - -# pluck is available, but will query all object fields first - -Comment.pluck(:title, :ratings) - -# To load the ids without loading the models - -Comment.where(author: "David Eddings").ids - -# To delete all the models of a relation - -Comment.where(ratings: 0).delete_all -``` - -## scopes - -Scopes can be written as class method, scope method is not implemented yet. -They can be chained as in AR or mixed with relation methods. - -```ruby -class Comment < CouchbaseOrm::Base - attribute :title, :string - attribute :author, :string - attribute :category, :string - attribute :ratings, :number - - def self.by_author(author) - where(author: author) - end -end - -Comment.by_author("Anne McCaffrey").where(category: 'S-F').not(ratings: 0).order(:title).limit(10) -``` - -## Operators - -Several operators are available to filter numerical results : \_gt, \_lt, \_gte, \_lte, \_ne - - ```ruby - Comment.where(ratings: {_gt: 3}) - ``` - -## Associations and Indexes - -There are common active record helpers available for use `belongs_to` and `has_many` - -```ruby - class Comment < CouchbaseOrm::Base - belongs_to :author - end - - class Author < CouchbaseOrm::Base - has_many :comments, dependent: :destroy - - # You can ensure an attribute is unique for this model - attribute :email, :string - ensure_unique :email - end -``` - -By default, `has_many` uses a view for association, -but you can define a `type` option to specify an association using N1QL instead: - - ```ruby - class Comment < CouchbaseOrm::Base - belongs_to :author - end - - class Author < CouchbaseOrm::Base - has_many :comments, type: :n1ql, dependent: :destroy - end - ``` - -## Nested - -Attributes can be of type nested, they must specify a type of NestedDocument. -The NestedValidation triggers nested validation on parent validation. - -```ruby - class Address < CouchbaseOrm::NestedDocument - attribute :road, :string - attribute :city, :string - validates :road, :city, presence: true - end - - class Author < CouchbaseOrm::Base - attribute :address, :nested, type: Address - validates :address, nested: true - end -``` - -Model can be queried using the nested attributes - -```ruby - Author.where(address: {road: '1 rue de la paix', city: 'Paris'}) -``` - -## Array - -Attributes can be of type array, they must contain something that can be serialized and deserialized to/from JSON. -You can enforce the type of array elements. The type can be a NestedDocument - -```ruby - class Book < CouchbaseOrm::NestedDocument - attribute :name, :string - validates :name, presence: true - end - - class Author < CouchbaseOrm::Base - attribute things, :array - attribute flags, :array, type: :string - attribute books, :array, type: Book - - validates :books, nested: true - end -``` - -## Performance Comparison with Couchbase-Ruby-Model - -Basically we migrated an application from [Couchbase Ruby Model](https://github.com/couchbase/couchbase-ruby-model) -to [Couchbase-ORM](https://github.com/acaprojects/couchbase-orm) (this project) - -- Rails 5 production -- Puma as the webserver -- Running on a 2015 Macbook Pro -- Performance test: `siege -c250 -r10 http://localhost:3000/auth/authority` - -The request above pulls the same database document each time and returns it. A simple O(1) operation. - -| Stat | Couchbase Ruby Model | Couchbase-ORM | -| :--- | :--- | :--- | -|Transactions|2500 hits|2500 hits| -|Elapsed time|12.24 secs|6.82 secs| -|Response time|0.88 secs|0.34 secs| -|Transaction rate|204.25 trans/sec|366.57 trans/sec| -|Request Code|[ruby-model-app](https://github.com/QuayPay/coauth/blob/95bbf5e5c3b3340e5af2da494b90c91c5e3d6eaa/app/controllers/auth/authorities_controller.rb#L6)|[couch-orm-app](https://github.com/QuayPay/coauth/blob/87f6fdeaab784ba252a5d38bbcf9e6b0477bb504/app/controllers/auth/authorities_controller.rb#L8)| +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE.