<commit_before># encoding: UTF-8

require 'pp'

$LOAD_PATH.unshift 'lib'
require 'montgomery'

class User
  # This class is saveable to the database
  include Montgomery::Entity

  # Add an #email method (similar to attr_reader) and tell Montgomery
  # that it should save the output of #email to the database
  montgomery_attr_reader :email
  # Add #role and #role= (like attr_accessor) and save output of role to the
  # database
  montgomery_attr_accessor :role

  # Initialize must accept a hash coming from MongoDB, it's up to you to
  # setup your object.
  # #id and #id= will be added automatically to handle BSON::ObjectId. #id= is
  # called after #initialize is done.
  def initialize(values={})
    @email = values['email']
    @role = values['role']
  end

  def places
    Places.find({user_id: id})
  end
end

class Place
  include Montgomery::Entity

  montgomery_attr_reader :name, :city, :district, :user_id

  def initialize(values={})
    @name = values['name']
    @city = values['city']
    @district = values['district']
  end

  # Someday some helpers might be added, like AR's belongs_to and has*
  def user
    Users.find_one({id: @user_id})
  end

  def user=(user)
    @user_id = user.id
  end
end

# assign collections to class-like constants
database = Montgomery::Connection.new.database('montgomery')
Users = database.collection('users')
Places = database.collection('places')

# clear the collections
Users.drop
Places.drop

# instantiate a user
user = User.new('email' => '<EMAIL>', 'password' => '<PASSWORD>')
pp user #=> a plain Ruby object, with @email=<EMAIL>, @password=<PASSWORD> and MongoDB's @_id
pp user.places #=> returns DB cursor
pp user.email #=> <EMAIL>
#pp user.password #=> raises NoMethodError

# store the user in the DB
Users.insert user
pp user.id #=> MongoDB document id

pp Users.count #=> 1
pp user.places.to_a #=> []

# create some places
my_places = []
my_places << Place.new('name' => 'ABC', 'user_id' => user.id, 'city' => 'Białystok', 'district' => 'Centrum')
my_places << Place.new('name' => 'Empik', 'user_id' => user.id, 'city' => 'Białystok', 'district' => 'Śródmieście')
my_places << Place.new('name' => 'Savona', 'user_id' => user.id, 'city' => 'Białystok', 'district' => 'Śródmieście')
my_places.each { |place| Places.insert place }

pp user.places.to_a

place = Places.find_one
pp place
pp place.user
pp place.name

# find can return a DB cursor or accept the block which will provide a
# cursor and will close it when leaving the block (similar to File.open)
places_cursor = Places.find
Places.find do |places|
 pp places.to_a
end

# you can do aggregations
pp Places.group(['city', 'district'], {}, {count: 0}, 'function(doc,
result) { result.count++; }')

# it's possible to update place's info
place.name = "Kotłownia"
Places.save(place)
pp Places.find_one({name: "Kotłownia"})

# run collection-wide update
Places.update({city: "Białystok"}, {'$set' => {district: 'Śródmieście'}})
# to see the changes in Ruby-land I need to load the object again
pp Places.find_one({name: "Kotłownia"}) #=> district is now 'Śródmieście'<commit_msg>example/simple.rb updated<commit_after># encoding: UTF-8

require 'pp'

$LOAD_PATH.unshift 'lib'
require 'montgomery'

class User
  # This class can be saved to the database. #id, #_id and #id=, #_id= will be
  # added to handle BSON::ObjectId. #_id= is called after #initialize is done.
  include Montgomery::Entity

  # Add an #email method (similar to attr_reader) and tell Montgomery
  # that it should save the return value of #email to the database
  montgomery_attr_reader :email
  montgomery_attr_reader :full_name, :password
  # Add #role and #role= (like attr_accessor) and save the return value of role
  # to the database
  montgomery_attr_accessor :role
  montgomery_attr_accessor :first_name, :last_name

  # Initialize must accept a hash (a MongoDB doc). It's up to you to
  # setup your object. There's no need to setup @_id, it will be done after
  # #initialize returns. However you can find it in values['_id'] if you need
  # it here.
  def initialize(values={})
    @email = values['email']
    @password = values['password']
    @role = values['role']
    @first_name = values['first_name']
    @last_name = values['last_name']
  end

  # Plain Ruby method. It's output will be saved to the DB - you can query by
  # full_name later. It might by a good idea to index such field.
  def full_name
    "#{first_name} #{last_name}"
  end

  def places
    Places.find({user_id: id})
  end
end

class Place
  include Montgomery::Entity

  montgomery_attr_accessor :name
  montgomery_attr_reader :city, :district, :user_id

  def initialize(values={})
    @name = values['name']
    @city = values['city']
    @district = values['district']
    if values.has_key?('user_id')
      @user_id = values['user_id']
    elsif values.has_key?('user')
      self.user = values['user']
    end
  end

  # Someday some helpers might be added, like AR's belongs_to and has*
  def user
    Users.find_one({id: @user_id})
  end

  def user=(user)
    @user_id = user.id
  end
end

puts "Connecting..."
# assign collections to class-like constants
database = Montgomery::Connection.new.database('montgomery')
Users = database.collection('users')
Places = database.collection('places')

puts "Droping collections..."
# clear the collections
Users.drop
Places.drop

puts "Creating a new user..."
user = User.new 'email' => '<EMAIL>',
  'password' => '<PASSWORD>',
  'first_name' => 'Wojciech',
  'last_name' => 'Piekutowski'
puts "User:"
pp user #=> a plain Ruby object, with @email=<EMAIL>, @password=<PASSWORD> and MongoDB's @_id
puts "User's places cursor:"
pp user.places #=> returns DB cursor
puts "User's email:"
pp user.email #=> <EMAIL>
#pp user.password #=> raises NoMethodError

puts "Inserting user..."
Users.insert user
puts "User id:"
pp user.id #=> MongoDB document id

puts "Users count:"
pp Users.count #=> 1
puts "User's places:"
pp user.places.to_a #=> []
puts "Users:"
pp Users.find.to_a #=> [#<User...>]

puts "Creating places..."
my_places = []
my_places << Place.new('name' => 'ABC', 'user' => user, 'city' => 'Białystok', 'district' => 'Centrum')
my_places << Place.new('name' => 'Empik', 'user' => user, 'city' => 'Białystok', 'district' => 'Śródmieście')
my_places << Place.new('name' => 'Savona', 'user' => user, 'city' => 'Białystok', 'district' => 'Śródmieście')
my_places.each { |place| Places.insert place }

puts "User's places:"
pp user.places.to_a

place = Places.find_one
puts "Place:"
pp place
puts "Place's user:"
pp place.user
puts "Place's name:"
pp place.name

# find can return a DB cursor or accept the block which will provide a
# cursor and will close it when leaving the block (similar to File.open)
places_cursor = Places.find
Places.find do |places|
  puts "Places:"
  pp places.to_a
end

# you can do aggregations
puts "Group places by city and district:"
pp Places.group(['city', 'district'], {}, {count: 0},
  'function(doc, result) { result.count++; }')

# it's possible to update place's info
puts "Updating place with name 'Kotłownia'..."
place.name = "Kotłownia"
Places.save(place)
puts "Updated place:"
pp Places.find_one({name: "Kotłownia"})

# run collection-wide update
puts "Updating places with {'$set' => {district: 'Śródmieście'}}..."
Places.update({city: "Białystok"}, {'$set' => {district: 'Śródmieście'}}, multi: true)

# to see the changes in Ruby-land you need to load the object again
puts "One of the updated places:"
pp Places.find_one({name: "Kotłownia"}) #=> district is now 'Śródmieście'

puts "User's places:"
pp user.places.to_a

puts "Creating a new user..."
user2 = User.new 'email' => '<EMAIL>',
  'password' => '<PASSWORD>',
  'first_name' => 'John',
  'last_name' => 'Doe'

puts "...and saving him..."
Users.save(user2)

puts "User found by full_name:"
pp user2 = Users.find_one({full_name: '<NAME>'})
puts "User's full name:"
pp user2.full_name
{'fraction_non_alphanumeric': 0.14147018030513175, 'fraction_numerical': 0.0012608750472828142, 'mean_word_length': 4.366711772665765, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '718779', 'n_tokens_mistral': 2786, 'n_tokens_neox': 2673, 'n_words': 884}