Sign inSign up

hollar/hollar-solidus

By hollar

•Updated about 6 years ago

Hollar Solidus Web Application and API

Image
0

10K+

hollar/hollar-solidus repository overview

⁠Run the project

⁠Option 1: Preferred and most performant

Running MySQL and Elasticsearch locally can be done easily with Docker⁠, however due to known performance issues with shared volumes, it is best to run ruby/rails locally outside of Docker.

# Requirements (one-time commands):
brew install mysql imagemagick wget

docker network create hollar
docker run -d --name elasticsearch-hollar \
  --network hollar \
  -p 9200:9200 -p 9300:9300 \
  elasticsearch:2-alpine
docker run -d --name mysql-hollar \
  --network hollar \
  -e MYSQL_ALLOW_EMPTY_PASSWORD=yes -e MYSQL_ROOT_PASSWORD="" \
  -p 3306:3306 \
  mysql
# Bootstrap a clean environment
# Running this will wipe your existing environment
./bin/bootstrap

# Start the application
./bin/start
# Site is now accessible via http://localhost:3000

Please note: After a reboot, you will need to start your MySQL and Elasticsearch services via:

docker start elasticsearch-hollar
docker start mysql-hollar
⁠Option 2: Least complex, 100% Docker

Running in 100% Docker is the easiest way to get up and running and is great for testing. However, due to known performance issues with Docker shared volumes and Docker for Mac/Win, it is the least performant option and certain requests will take 2x-5x longer than those in Option 1.

docker network create hollar
docker-compose run --rm web ./bin/bootstrap
docker-compose up -d
# Site is now accessible via http://localhost:33000

⁠Important Architectural Note

This is an object-oriented codebase, built on top of a typical Rails application (Solidus) which provides the vast majority of domain logic in the application. That is an introduction to this, our one architectural rule:

⁠SERVICE OBJECTS IMPLEMENTING THE COMMAND PATTERN ARE PROHIBITED

Now, let me explain. I like the idea of a service-oriented architecture, and appreciate that it provides many benefits, such as refactoring, decoupling, testing, small source files and so on. However, the basic fact is that while a service-oriented architecture has many practical benefits, this application is in fact not service-oriented. It is fundamentally, overwhelmingly, object-oriented.

Attempting to cross the streams of these two design philosophies by creating "service object" classes by extracting methods into classes (the "Command" pattern) is an extremely popular technique that, undermines existing object-oriented design. Domain services in Domain-Driven Design are supposed to be stateless standalone interfaces, so the correct implementation choice in Ruby for that interface is a module, not a class. Therefore we should be writing domain modules, not domain objects.

And Rails, in fact, has a solution for how to create and integrate such modules and address the same problems of code organization, which is ActiveSupport concerns. ActiveRecord is inherently a system for combining data persistence and domain logic in a single class, which is to say it is fundamental to the classic design of Rails applications. Concerns maintain that design while providing the architectural benefits of domain services.

On the other hand, using objects for domain services introduces service-orientation into the object-oriented paradigm of a language which never meant to support liberating methods from their natural context. It explodes the number of concepts and classes by removing every complex method from an object. It removes logic from objects with the goal of making them dumb data structures, which, again, is antithetical to the concept of ActiveRecord. And most problematic of all, it moves us towards a design goal we can never actually reach, because we are building on top of Solidus, which is not built with that design, which is on top of Rails, which makes it difficult to support that design. I can see where this design structure is useful, but this is not the case for this application.

The following thoughts are warning signs that you are about to create a service object that doesn't belong in this app:

  • This logic deserves its own class.
  • There's lots of room in the class namespace.
  • It's useful to move code away from its subject.
  • Class methods aren't as cool as instance methods.
  • It's much cleaner to have a tiny object for everything.
  • A stateless standalone interface should be modeled as a class.
  • When you think about it, methods have their own data structure.
  • This post from the Code Climate blog makes a lot of good points.
  • Wow, lots of memoized state lets you remove function parameters!
  • What does this object do? It, uh, performs. And nothing else. Just performs.
  • Ruby couples what is done with who does it, but the Command pattern fixes it!
  • It's easier to test and refactor a class, so really, everything should be a class.
  • Now I'll write a method named call, even though this class isn't Rack middleware.
  • This method's return type is too complex, so I'll create a class out of the method.
  • There are a lot more lines now, but most of them are boilerplate so they don't count.

Now for the good news. We have a great place to create a service-oriented architecture, which is our Elixir repo. Elixir, being unburdened with the object-oriented paradigm at the language level, is actually a fantastic environment for service-oriented architecture, CQRS, plugs and adapters -- really, all the best ideas of application design from the last fifteen years. Because they really are useful and practical, but are difficult to combine with object-oriented design, and after experiencing writing them in Elixir, that will be more apparent.

So to sum up:

  • Avoid the reflex to apply the Command pattern because its use damages Rails object orientated design principles.
  • Please try to maintain the object-oriented paradigm represented in the rest of Solidus.
  • Write business code in the model first and extract it into a separate location later if necessary.
  • If you want to factor code out of a model, use an ActiveSupport Concern.
⁠For further reading

Finally, yes, there is some code in this repo which predates and violates this rule. That's why this note was added.

⁠Social Login

Social login stuff really depends on the port that your localhost is running on. It would be amazing if this was set to 2388.

⁠Braintree Payments

List of credit cards to use⁠ Sandbox control panel⁠

On your local development server, in Spree admin, you need to add a Payment Method in Configuration and provide this information:

Provider    : Spree::Gateway::BraintreeGateway
Name        : (Choose a descriptive name, like "Braintree Development")
Environment : sandbox
Display     : Both
Active      : Yes
Merchant ID : Check the .env File
Public Key  : Check the .env File
Private Key : Check the .env File
Client SEK  : Check the .env File

The merchant ID goes under the "Merchant" field in Solidus. The differance between a Merchant ID and Merchant Account ID can be found in the following Braintree docs⁠

⁠Background Processing

To run the background workers against Amazon SQS:

bundle exec shoryuken -R -C config/shoryuken.yml

There are 4 configured queues for each environment (low, medium, high and urgent), e.g:

  • development_low_priority
  • development_medium_priority
  • development_high_priority
  • development_urgent_priority

The local queues are therefore shared across all developers and job processing may crisscross if multiple developers are running the workers concurrently. Local separation of queues might be possible in the near future.

⁠Database

The config/database.yml was removed from github. In order to have a proper database file, we can copy the config/database.yml.example file to the config/database.yml file.

⁠Filemaker Importation

We can import the data from Filemaker into our own database in 2 different way. Both of those import way are achieve via rake tasks.

⁠Import products directly from Filemaker

To import the data directly from Filemaker without any filter whatsoever. The following command can be run

`rake 'filemaker:importer:all_products'`

This will import all the products from Filemaker. Please be aware that this can take a long time since there are over 10 000 products right now into Filemaker.

If you wish to import only a partial amount for testing locally you can add an optional argument to the task

`rake 'filemaker:importer:all_products[10]'`

This would import the first 10 products it finds.

⁠Import products from a CSV file

If you wish to simply import specific SKUs of Filemaker, you can create a {whatever_name}.csv file in the following directory tmp/import/csv/. Within that file, you must specify one SKU per line. Once that done run the following command

`rake 'filemaker:importer:all_products_from_csv'`

It will check for every line and do a single query to Filemaker that will retrieve all the searched products. By default, once it's done it will archive the file that you have just imported to the tmp/import/csv/archive folder. That way you know when the file has been parsed correctly and makes it that you can't run the same file multiple time on a production server.

If you do not wish to archive the file you can run the previous command with an additional parameter.

`rake 'filemaker:importer:all_products_from_csv[false]'`

This will not put the file in the archive folder. This is useful when developing a feature and you have to constantly wipe your database.

Please Note: You can create as many .csv file in that directory and run the command and it will import every SKU from every file in one time.

⁠Import images from file in directory

Since we do not know how Filemaker processes the images yet. There is a rake task that let's you import image manually.

`rake 'filemaker:importer:import_images'`

It will look at the SKU of the image picture that resides in tmp/import/images and set the product image accordingly. This process may take a long time since it upload the image in multiple format to S3.

By default it archives. If you do not which to archive processed images, please set the flag to false

`rake 'filemaker:importer:import_images[false]'`

⁠API queries

In order to easily reproduce API calls made from the apps. (Android / IOS) we have setup Postman to easily make to queries.

⁠How to get Postman

There are two ways to get Postman here: https://www.getpostman.com/⁠

⁠How to sync up
  1. Make sure that you already have postman installed
  2. Click the wrench icon at the top right corner of the application
  3. Click the Data tab
  4. Import the Backup.postman_dump.json file from the api_queries folder of the project
  5. Done!
⁠Alternative (PAW)

Some of us also uses PAW. In order to import postman data in PAW, please follow the following URL: https://luckymarmot.com/paw/doc/migrate/postman⁠

General Notes:

  1. Giftcard products are only mildly special/different from general products.
  2. They need to have a product name that is: Gift Card.
  3. The Gift Card needs to have a slug named: gift-card
  4. The price of the gift card doesn't matter since we will pass price attributes to the line item
  5. Gift Card products should have variants, this is what is selected in the UI
  6. Gift Card products need a product property of 'Unsearchable' as to not appear in product search/feeds (set to true)
  7. IF Setting a gift card up on a development machine, use the giftcard.seeds file.
  8. The ' character are mandatory in order for the command to run. There are some shells that doesn't not correctly supports argument within a rake task and having the single quote makes it parse correctly.
  9. If you wish to import data locally from Filemaker and you do not care about the images (which is the part that takes the most time), you can simply comment out the line process_image(base_product, product) from build_product and build_variant of /app/domain/importer/product/product_factory.rb.
  10. For more detail, please check the lib/tasks/importer.rake rake task file.
  11. Turbolinks was removed from the project at sha 0b975bde152853a7b78f9f513774bda4c2121a7b
  12. All code moving forward, especially concerning JS should not target Turbolink events

Tag summary

Content type

Image

Digest

Size

429.6 MB

Last updated

about 9 years ago

docker pull hollar/hollar-solidus